<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
  <meta name="theme-color" content="#222">
  <meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/safari-pinned-tab.svg" color="#222">
  <link rel="stylesheet" href="/css/main.css">
  <link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib/pace/pace-theme-minimal.min.css">
  <script src="/lib/pace/pace.min.js"></script>
  <script id="hexo-configurations">
    var NexT = window.NexT ||
    {};
    var CONFIG = {
      "hostname": "cuiqingcai.com",
      "root": "/",
      "scheme": "Pisces",
      "version": "7.8.0",
      "exturl": false,
      "sidebar":
      {
        "position": "right",
        "width": 360,
        "display": "post",
        "padding": 18,
        "offset": 12,
        "onmobile": false,
        "widgets": [
          {
            "type": "image",
            "name": "阿布云",
            "enable": false,
            "url": "https://www.abuyun.com/http-proxy/introduce.html",
            "src": "https://qiniu.cuiqingcai.com/88au8.jpg",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "天验",
            "enable": true,
            "url": "https://tutorial.lengyue.video/?coupon=12ef4b1a-a3db-11ea-bb37-0242ac130002_cqx_850",
            "src": "https://qiniu.cuiqingcai.com/bco2a.png",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "华为云",
            "enable": false,
            "url": "https://activity.huaweicloud.com/2020_618_promotion/index.html?bpName=5f9f98a29e2c40b780c1793086f29fe2&bindType=1&salesID=wangyubei",
            "src": "https://qiniu.cuiqingcai.com/y42ik.jpg",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "张小鸡",
            "enable": false,
            "url": "http://www.zxiaoji.com/",
            "src": "https://qiniu.cuiqingcai.com/fm72f.png",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "Luminati",
            "src": "https://qiniu.cuiqingcai.com/ikkq9.jpg",
            "url": "https://luminati-china.io/?affiliate=ref_5fbbaaa9647883f5c6f77095",
            "width": "100%",
            "enable": false
      },
          {
            "type": "image",
            "name": "IPIDEA",
            "url": "http://www.ipidea.net/?utm-source=cqc&utm-keyword=?cqc",
            "src": "https://qiniu.cuiqingcai.com/0ywun.png",
            "width": "100%",
            "enable": true
      },
          {
            "type": "tags",
            "name": "标签云",
            "enable": true
      },
          {
            "type": "categories",
            "name": "分类",
            "enable": true
      },
          {
            "type": "friends",
            "name": "友情链接",
            "enable": true
      },
          {
            "type": "hot",
            "name": "猜你喜欢",
            "enable": true
      }]
      },
      "copycode":
      {
        "enable": true,
        "show_result": true,
        "style": "mac"
      },
      "back2top":
      {
        "enable": true,
        "sidebar": false,
        "scrollpercent": true
      },
      "bookmark":
      {
        "enable": false,
        "color": "#222",
        "save": "auto"
      },
      "fancybox": false,
      "mediumzoom": false,
      "lazyload": false,
      "pangu": true,
      "comments":
      {
        "style": "tabs",
        "active": "gitalk",
        "storage": true,
        "lazyload": false,
        "nav": null,
        "activeClass": "gitalk"
      },
      "algolia":
      {
        "hits":
        {
          "per_page": 10
        },
        "labels":
        {
          "input_placeholder": "Search for Posts",
          "hits_empty": "We didn't find any results for the search: ${query}",
          "hits_stats": "${hits} results found in ${time} ms"
        }
      },
      "localsearch":
      {
        "enable": true,
        "trigger": "auto",
        "top_n_per_article": 10,
        "unescape": false,
        "preload": false
      },
      "motion":
      {
        "enable": false,
        "async": false,
        "transition":
        {
          "post_block": "bounceDownIn",
          "post_header": "slideDownIn",
          "post_body": "slideDownIn",
          "coll_header": "slideLeftIn",
          "sidebar": "slideUpIn"
        }
      },
      "path": "search.xml"
    };

  </script>
  <meta name="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
  <meta property="og:type" content="website">
  <meta property="og:title" content="静觅">
  <meta property="og:url" content="https://cuiqingcai.com/page/18/index.html">
  <meta property="og:site_name" content="静觅">
  <meta property="og:description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
  <meta property="og:locale" content="zh_CN">
  <meta property="article:author" content="崔庆才">
  <meta property="article:tag" content="崔庆才">
  <meta property="article:tag" content="静觅">
  <meta property="article:tag" content="PHP">
  <meta property="article:tag" content="Java">
  <meta property="article:tag" content="Python">
  <meta property="article:tag" content="Spider">
  <meta property="article:tag" content="爬虫">
  <meta property="article:tag" content="Web">
  <meta property="article:tag" content="Kubernetes">
  <meta property="article:tag" content="深度学习">
  <meta property="article:tag" content="机器学习">
  <meta property="article:tag" content="数据分析">
  <meta property="article:tag" content="网络">
  <meta property="article:tag" content="IT">
  <meta property="article:tag" content="技术">
  <meta property="article:tag" content="博客">
  <meta name="twitter:card" content="summary">
  <link rel="canonical" href="https://cuiqingcai.com/page/18/">
  <script id="page-configurations">
    // https://hexo.io/docs/variables.html
    CONFIG.page = {
      sidebar: "",
      isHome: true,
      isPost: false,
      lang: 'zh-CN'
    };

  </script>
  <title>静觅丨崔庆才的个人站点</title>
  <meta name="google-site-verification" content="p_bIcnvirkFzG2dYKuNDivKD8-STet5W7D-01woA2fc" />
  <noscript>
    <style>
      .use-motion .brand,
      .use-motion .menu-item,
      .sidebar-inner,
      .use-motion .post-block,
      .use-motion .pagination,
      .use-motion .comments,
      .use-motion .post-header,
      .use-motion .post-body,
      .use-motion .collection-header
      {
        opacity: initial;
      }

      .use-motion .site-title,
      .use-motion .site-subtitle
      {
        opacity: initial;
        top: initial;
      }

      .use-motion .logo-line-before i
      {
        left: initial;
      }

      .use-motion .logo-line-after i
      {
        right: initial;
      }

    </style>
  </noscript>
  <link rel="alternate" href="/atom.xml" title="静觅" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container">
    <div class="headband"></div>
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner">
        <div class="site-brand-container">
          <div class="site-nav-toggle">
            <div class="toggle" aria-label="切换导航栏">
              <span class="toggle-line toggle-line-first"></span>
              <span class="toggle-line toggle-line-middle"></span>
              <span class="toggle-line toggle-line-last"></span>
            </div>
          </div>
          <div class="site-meta">
            <a href="/" class="brand" rel="start">
              <span class="logo-line-before"><i></i></span>
              <h1 class="site-title">静觅 <span class="site-subtitle"> 崔庆才的个人站点 </span>
              </h1>
              <span class="logo-line-after"><i></i></span>
            </a>
          </div>
          <div class="site-nav-right">
            <div class="toggle popup-trigger">
              <i class="fa fa-search fa-fw fa-lg"></i>
            </div>
          </div>
        </div>
        <nav class="site-nav">
          <ul id="menu" class="main-menu menu">
            <li class="menu-item menu-item-home">
              <a href="/" rel="section">首页</a>
            </li>
            <li class="menu-item menu-item-archives">
              <a href="/archives/" rel="section">文章列表</a>
            </li>
            <li class="menu-item menu-item-tags">
              <a href="/tags/" rel="section">文章标签</a>
            </li>
            <li class="menu-item menu-item-categories">
              <a href="/categories/" rel="section">文章分类</a>
            </li>
            <li class="menu-item menu-item-about">
              <a href="/about/" rel="section">关于博主</a>
            </li>
            <li class="menu-item menu-item-message">
              <a href="/message/" rel="section">给我留言</a>
            </li>
            <li class="menu-item menu-item-search">
              <a role="button" class="popup-trigger">搜索 </a>
            </li>
          </ul>
        </nav>
        <div class="search-pop-overlay">
          <div class="popup search-popup">
            <div class="search-header">
              <span class="search-icon">
                <i class="fa fa-search"></i>
              </span>
              <div class="search-input-container">
                <input autocomplete="off" autocapitalize="off" placeholder="搜索..." spellcheck="false" type="search" class="search-input">
              </div>
              <span class="popup-btn-close">
                <i class="fa fa-times-circle"></i>
              </span>
            </div>
            <div id="search-result">
              <div id="no-result">
                <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
              </div>
            </div>
          </div>
        </div>
      </div>
    </header>
    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span>0%</span>
    </div>
    <div class="reading-progress-bar"></div>
    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div class="content index posts-expand">
            <div class="carousel">
              <div id="wowslider-container">
                <div class="ws_images">
                  <ul>
                    <li><a target="_blank" href="https://cuiqingcai.com/5052.html"><img title="Python3网络爬虫开发实战教程" src="https://qiniu.cuiqingcai.com/ipy96.jpg" /></a></li>
                    <li><a target="_blank" href="https://t.lagou.com/fRCBRsRCSN6FA"><img title="52讲轻松搞定网络爬虫" src="https://qiniu.cuiqingcai.com/fqq5e.png" /></a></li>
                    <li><a target="_blank" href="https://brightdata.grsm.io/cuiqingcai"><img title="亮网络解锁器" src="https://qiniu.cuiqingcai.com/6qnb7.png" /></a></li>
                    <li><a target="_blank" href="https://cuiqingcai.com/4320.html"><img title="Python3网络爬虫开发视频教程" src="https://qiniu.cuiqingcai.com/bjrny.jpg" /></a></li>
                    <li><a target="_blank" href="https://cuiqingcai.com/5094.html"><img title="爬虫代理哪家强？十大付费代理详细对比评测出炉！" src="https://qiniu.cuiqingcai.com/nifs6.jpg" /></a></li>
                  </ul>
                </div>
                <div class="ws_thumbs">
                  <div>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/ipy96.jpg" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/fqq5e.png" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/6qnb7.png" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/bjrny.jpg" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/nifs6.jpg" /></a>
                  </div>
                </div>
                <div class="ws_shadow"></div>
              </div>
            </div>
            <link rel="stylesheet" href="/lib/wowslide/slide.css">
            <script src="/lib/wowslide/jquery.min.js"></script>
            <script src="/lib/wowslide/slider.js"></script>
            <script>
              jQuery("#wowslider-container").wowSlider(
              {
                effect: "cube",
                prev: "",
                next: "",
                duration: 20 * 100,
                delay: 20 * 100,
                width: 716,
                height: 297,
                autoPlay: true,
                playPause: true,
                stopOnHover: false,
                loop: false,
                bullets: 0,
                caption: true,
                captionEffect: "slide",
                controls: true,
                onBeforeStep: 0,
                images: 0
              });

            </script>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5508.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5508.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 3.1.3-解析链接</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>前面说过，urllib库里还提供了<code>parse</code>这个模块，它定义了处理URL的标准接口，例如实现URL各部分的抽取、合并以及链接转换。它支持如下协议的URL处理：file、ftp、gopher、hdl、http、https、imap、mailto、 mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、 sip、sips、snews、svn、svn+ssh、telnet和wais。本节中，我们介绍一下该模块中常用的方法来看一下它的便捷之处。</p>
                  <h2 id="1-urlparse"><a href="#1-urlparse" class="headerlink" title="1. urlparse()"></a>1. <code>urlparse()</code></h2>
                  <p>该方法可以实现URL的识别和分段，这里先用一个实例来看一下：</p>
                  <figure class="highlight nimrod">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> urlparse</span><br><span class="line"></span><br><span class="line"><span class="literal">result</span> = urlparse('http://www.baidu.com/index.html;user?id=<span class="number">5</span><span class="comment">#comment')</span></span><br><span class="line">print(<span class="keyword">type</span>(<span class="literal">result</span>), <span class="literal">result</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们利用<code>urlparse()</code>方法进行了一个URL的解析。首先，输出了解析结果的类型，然后将结果也输出出来。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;class <span class="string">'urllib.parse.ParseResult'</span>&gt;</span><br><span class="line">ParseResult(<span class="attribute">scheme</span>=<span class="string">'http'</span>, <span class="attribute">netloc</span>=<span class="string">'www.baidu.com'</span>, <span class="attribute">path</span>=<span class="string">'/index.html'</span>, <span class="attribute">params</span>=<span class="string">'user'</span>, <span class="attribute">query</span>=<span class="string">'id=5'</span>, <span class="attribute">fragment</span>=<span class="string">'comment'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，返回结果是一个<code>ParseResult</code>类型的对象，它包含6部分，分别是<code>scheme</code>、<code>netloc</code>、<code>path</code>、<code>params</code>、<code>query</code>和<code>fragment</code>。</p>
                  <p>观察一下该实例的URL：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">http://www.baidu.com/<span class="keyword">index</span>.html;<span class="keyword">user</span>?id=<span class="number">5</span>#<span class="keyword">comment</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，<code>urlparse()</code>方法将其拆分成了6部分。大体观察可以发现，解析时有特定的分隔符。比如，://前面的就是<code>scheme</code>，代表协议；第一个/前面便是<code>netloc</code>，即域名；分号;前面是<code>params</code>，代表参数。</p>
                  <p>所以，可以得出一个标准的链接格式，具体如下：</p>
                  <figure class="highlight crystal">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">scheme:</span>/<span class="regexp">/netloc/path</span>;parameters?query<span class="comment">#fragment</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>一个标准的URL都会符合这个规则，利用<code>urlparse()</code>方法可以将它拆分开来。</p>
                  <p>除了这种最基本的解析方式外，<code>urlparse()</code>方法还有其他配置吗？接下来，看一下它的API用法：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">urllib.parse.urlparse(urlstring, <span class="attribute">scheme</span>=<span class="string">''</span>, <span class="attribute">allow_fragments</span>=<span class="literal">True</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，它有3个参数。</p>
                  <ul>
                    <li><strong><code>urlstring</code></strong>：这是必填项，即待解析的URL。</li>
                    <li><strong><code>scheme</code></strong>：它是默认的协议（比如<code>http</code>或<code>https</code>等）。假如这个链接没有带协议信息，会将这个作为默认的协议。我们用实例来看一下：</li>
                  </ul>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse import urlparse</span><br><span class="line"></span><br><span class="line">result = urlparse(<span class="string">'www.baidu.com/index.html;user?id=5#comment'</span>, <span class="attribute">scheme</span>=<span class="string">'https'</span>)</span><br><span class="line"><span class="builtin-name">print</span>(result)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ParseResult(<span class="attribute">scheme</span>=<span class="string">'https'</span>, <span class="attribute">netloc</span>=<span class="string">''</span>, <span class="attribute">path</span>=<span class="string">'www.baidu.com/index.html'</span>, <span class="attribute">params</span>=<span class="string">'user'</span>, <span class="attribute">query</span>=<span class="string">'id=5'</span>, <span class="attribute">fragment</span>=<span class="string">'comment'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，我们提供的URL没有包含最前面的<code>scheme</code>信息，但是通过指定默认的<code>scheme</code>参数，返回的结果是<code>https</code>。</p>
                  <p>假设我们带上了<code>scheme</code>：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">result</span> = urlparse(<span class="string">'http://www.baidu.com/index.html;user?id=5#comment'</span>, scheme=<span class="string">'https'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>则结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ParseResult(<span class="attribute">scheme</span>=<span class="string">'http'</span>, <span class="attribute">netloc</span>=<span class="string">'www.baidu.com'</span>, <span class="attribute">path</span>=<span class="string">'/index.html'</span>, <span class="attribute">params</span>=<span class="string">'user'</span>, <span class="attribute">query</span>=<span class="string">'id=5'</span>, <span class="attribute">fragment</span>=<span class="string">'comment'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可见，<code>scheme</code>参数只有在URL中不包含<code>scheme</code>信息时才生效。如果URL中有<code>scheme</code>信息，就会返回解析出的<code>scheme</code>。</p>
                  <ul>
                    <li><strong><code>allow_fragments</code></strong>：即是否忽略<code>fragment</code>。如果它被设置为<code>False</code>，<code>fragment</code>部分就会被忽略，它会被解析为<code>path</code>、<code>parameters</code>或者<code>query</code>的一部分，而<code>fragment</code>部分为空。下面我们用实例来看一下：</li>
                  </ul>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse import urlparse</span><br><span class="line"></span><br><span class="line">result = urlparse(<span class="string">'http://www.baidu.com/index.html;user?id=5#comment'</span>, <span class="attribute">allow_fragments</span>=<span class="literal">False</span>)</span><br><span class="line"><span class="builtin-name">print</span>(result)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ParseResult(<span class="attribute">scheme</span>=<span class="string">'http'</span>, <span class="attribute">netloc</span>=<span class="string">'www.baidu.com'</span>, <span class="attribute">path</span>=<span class="string">'/index.html'</span>, <span class="attribute">params</span>=<span class="string">'user'</span>, <span class="attribute">query</span>=<span class="string">'id=5#comment'</span>, <span class="attribute">fragment</span>=<span class="string">''</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>假设URL中不包含<code>params</code>和<code>query</code>，我们再通过实例看一下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse import urlparse</span><br><span class="line"></span><br><span class="line">result = urlparse(<span class="string">'http://www.baidu.com/index.html#comment'</span>, <span class="attribute">allow_fragments</span>=<span class="literal">False</span>)</span><br><span class="line"><span class="builtin-name">print</span>(result)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ParseResult(<span class="attribute">scheme</span>=<span class="string">'http'</span>, <span class="attribute">netloc</span>=<span class="string">'www.baidu.com'</span>, <span class="attribute">path</span>=<span class="string">'/index.html#comment'</span>, <span class="attribute">params</span>=<span class="string">''</span>, <span class="attribute">query</span>=<span class="string">''</span>, <span class="attribute">fragment</span>=<span class="string">''</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，当URL中不包含<code>params</code>和<code>query</code>时，<code>fragment</code>便会被解析为<code>path</code>的一部分。</p>
                  <p>返回结果<code>ParseResult</code>实际上是一个元组，我们可以用索引顺序来获取，也可以用属性名获取。示例如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse import urlparse</span><br><span class="line"></span><br><span class="line">result = urlparse(<span class="string">'http://www.baidu.com/index.html#comment'</span>, <span class="attribute">allow_fragments</span>=<span class="literal">False</span>)</span><br><span class="line"><span class="builtin-name">print</span>(result.scheme, result[0], result.netloc, result[1], <span class="attribute">sep</span>=<span class="string">'\n'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们分别用索引和属性名获取了<code>scheme</code>和<code>netloc</code>，其运行结果如下：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">http</span></span><br><span class="line"><span class="selector-tag">http</span></span><br><span class="line"><span class="selector-tag">www</span><span class="selector-class">.baidu</span><span class="selector-class">.com</span></span><br><span class="line"><span class="selector-tag">www</span><span class="selector-class">.baidu</span><span class="selector-class">.com</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，二者的结果是一致的，两种方法都可以成功获取。</p>
                  <h2 id="2-urlunparse"><a href="#2-urlunparse" class="headerlink" title="2. urlunparse()"></a>2. <code>urlunparse()</code></h2>
                  <p>有了<code>urlparse()</code>，相应地就有了它的对立方法<code>urlunparse()</code>。它接受的参数是一个可迭代对象，但是它的长度必须是6，否则会抛出参数数量不足或者过多的问题。先用一个实例看一下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import urlunparse</span><br><span class="line"></span><br><span class="line">data = [<span class="string">'http'</span>, <span class="string">'www.baidu.com'</span>, <span class="string">'index.html'</span>, <span class="string">'user'</span>, <span class="string">'a=6'</span>, <span class="string">'comment'</span>]</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urlunparse(data)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里参数<code>data</code>用了列表类型。当然，你也可以用其他类型，比如元组或者特定的数据结构。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight dts">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">http:</span><span class="comment">//www.baidu.com/index.html;user?a=6#comment</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样我们就成功实现了URL的构造。</p>
                  <h2 id="3-urlsplit"><a href="#3-urlsplit" class="headerlink" title="3. urlsplit()"></a>3. <code>urlsplit()</code></h2>
                  <p>这个方法和<code>urlparse()</code>方法非常相似，只不过它不再单独解析<code>params</code>这一部分，只返回5个结果。上面例子中的<code>params</code>会合并到<code>path</code>中。示例如下：</p>
                  <figure class="highlight isbl">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="variable">from</span> <span class="variable">urllib.parse</span> <span class="variable">import</span> <span class="variable">urlsplit</span></span><br><span class="line"></span><br><span class="line"><span class="variable"><span class="class">result</span></span> = <span class="function"><span class="title">urlsplit</span>(<span class="string">'http://www.baidu.com/index.html;user?id=5#comment'</span>)</span></span><br><span class="line"><span class="function"><span class="title">print</span>(<span class="variable"><span class="class">result</span></span>)</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="constructor">SplitResult(<span class="params">scheme</span>='<span class="params">http</span>', <span class="params">netloc</span>='<span class="params">www</span>.<span class="params">baidu</span>.<span class="params">com</span>', <span class="params">path</span>='<span class="operator">/</span><span class="params">index</span>.<span class="params">html</span>;<span class="params">user</span>', <span class="params">query</span>='<span class="params">id</span>=5', <span class="params">fragment</span>='<span class="params">comment</span>')</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，返回结果是<code>SplitResult</code>，它其实也是一个元组类型，既可以用属性获取值，也可以用索引来获取。示例如下：</p>
                  <figure class="highlight nimrod">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> urlsplit</span><br><span class="line"></span><br><span class="line"><span class="literal">result</span> = urlsplit('http://www.baidu.com/index.html;user?id=<span class="number">5</span><span class="comment">#comment')</span></span><br><span class="line">print(<span class="literal">result</span>.scheme, <span class="literal">result</span>[<span class="number">0</span>])</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight livecodeserver">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">http</span> <span class="keyword">http</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="4-urlunsplit"><a href="#4-urlunsplit" class="headerlink" title="4. urlunsplit()"></a>4. <code>urlunsplit()</code></h2>
                  <p>与<code>urlunparse()</code>类似，它也是将链接各个部分组合成完整链接的方法，传入的参数也是一个可迭代对象，例如列表、元组等，唯一的区别是长度必须为5。示例如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import urlunsplit</span><br><span class="line"></span><br><span class="line">data = [<span class="string">'http'</span>, <span class="string">'www.baidu.com'</span>, <span class="string">'index.html'</span>, <span class="string">'a=6'</span>, <span class="string">'comment'</span>]</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urlunsplit(data)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight dts">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">http:</span><span class="comment">//www.baidu.com/index.html?a=6#comment</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="5-urljoin"><a href="#5-urljoin" class="headerlink" title="5. urljoin()"></a>5. <code>urljoin()</code></h2>
                  <p>有了<code>urlunparse()</code>和<code>urlunsplit()</code>方法，我们可以完成链接的合并，不过前提必须要有特定长度的对象，链接的每一部分都要清晰分开。</p>
                  <p>此外，生成链接还有另一个方法，那就是<code>urljoin()</code>方法。我们可以提供一个<code>base_url</code>（基础链接）作为第一个参数，将新的链接作为第二个参数，该方法会分析<code>base_url</code>的<code>scheme</code>、<code>netloc</code>和<code>path</code>这3个内容并对新链接缺失的部分进行补充，最后返回结果。</p>
                  <p>下面通过几个实例看一下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import urljoin</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com'</span>, <span class="string">'FAQ.html'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com'</span>, <span class="string">'https://cuiqingcai.com/FAQ.html'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com/about.html'</span>, <span class="string">'https://cuiqingcai.com/FAQ.html'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com/about.html'</span>, <span class="string">'https://cuiqingcai.com/FAQ.html?question=2'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com?wd=abc'</span>, <span class="string">'https://cuiqingcai.com/index.php'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'http://www.baidu.com'</span>, <span class="string">'?category=2#comment'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'www.baidu.com'</span>, <span class="string">'?category=2#comment'</span>)</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(urljoin(<span class="string">'www.baidu.com#comment'</span>, <span class="string">'?category=2'</span>)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight dts">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">http:</span><span class="comment">//www.baidu.com/FAQ.html</span></span><br><span class="line"><span class="symbol">https:</span><span class="comment">//cuiqingcai.com/FAQ.html</span></span><br><span class="line"><span class="symbol">https:</span><span class="comment">//cuiqingcai.com/FAQ.html</span></span><br><span class="line"><span class="symbol">https:</span><span class="comment">//cuiqingcai.com/FAQ.html?question=2</span></span><br><span class="line"><span class="symbol">https:</span><span class="comment">//cuiqingcai.com/index.php</span></span><br><span class="line"><span class="symbol">http:</span><span class="comment">//www.baidu.com?category=2#comment</span></span><br><span class="line">www.baidu.com?category=<span class="number">2</span><span class="meta">#comment</span></span><br><span class="line">www.baidu.com?category=<span class="number">2</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，<code>base_url</code>提供了三项内容<code>scheme</code>、<code>netloc</code>和<code>path</code>。如果这3项在新的链接里不存在，就予以补充；如果新的链接存在，就使用新的链接的部分。而<code>base_url</code>中的<code>params</code>、<code>query</code>和<code>fragment</code>是不起作用的。</p>
                  <p>通过<code>urljoin()</code>方法，我们可以轻松实现链接的解析、拼合与生成。</p>
                  <h2 id="6-urlencode"><a href="#6-urlencode" class="headerlink" title="6. urlencode()"></a>6. <code>urlencode()</code></h2>
                  <p>这里我们再介绍一个常用的方法——<code>urlencode()</code>，它在构造GET请求参数的时候非常有用，示例如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import urlencode</span><br><span class="line"></span><br><span class="line">params = &#123;</span><br><span class="line">    <span class="string">'name'</span>: <span class="string">'germey'</span>,</span><br><span class="line">    <span class="string">'age'</span>: <span class="number">22</span></span><br><span class="line">&#125;</span><br><span class="line">base_url = <span class="string">'http://www.baidu.com?'</span></span><br><span class="line">url = base_url + urlencode(params)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(url)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里首先声明了一个字典来将参数表示出来，然后调用<code>urlencode()</code>方法将其序列化为GET请求参数。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight dts">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">http:</span><span class="comment">//www.baidu.com?name=germey&amp;age=22</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，参数就成功地由字典类型转化为GET请求参数了。</p>
                  <p>这个方法非常常用。有时为了更加方便地构造参数，我们会事先用字典来表示。要转化为URL的参数时，只需要调用该方法即可。</p>
                  <h2 id="7-parse-qs"><a href="#7-parse-qs" class="headerlink" title="7. parse_qs()"></a>7. <code>parse_qs()</code></h2>
                  <p>有了序列化，必然就有反序列化。如果我们有一串GET请求参数，利用<code>parse_qs()</code>方法，就可以将它转回字典，示例如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import parse_qs</span><br><span class="line"></span><br><span class="line">query = <span class="string">'name=germey&amp;age=22'</span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(parse_qs(query)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight prolog">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;<span class="string">'name'</span>: [<span class="string">'germey'</span>], <span class="string">'age'</span>: [<span class="string">'22'</span>]&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，这样就成功转回为字典类型了。</p>
                  <h2 id="8-parse-qsl"><a href="#8-parse-qsl" class="headerlink" title="8. parse_qsl()"></a>8. <code>parse_qsl()</code></h2>
                  <p>另外，还有一个<code>parse_qsl()</code>方法，它用于将参数转化为元组组成的列表，示例如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import parse_qsl</span><br><span class="line"></span><br><span class="line">query = <span class="string">'name=germey&amp;age=22'</span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(parse_qsl(query)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[(<span class="symbol">'name</span>', <span class="symbol">'germey</span>'), (<span class="symbol">'age</span>', <span class="symbol">'22</span>')]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，运行结果是一个列表，而列表中的每一个元素都是一个元组，元组的第一个内容是参数名，第二个内容是参数值。</p>
                  <h2 id="9-quote"><a href="#9-quote" class="headerlink" title="9. quote()"></a>9. <code>quote()</code></h2>
                  <p>该方法可以将内容转化为URL编码的格式。URL中带有中文参数时，有时可能会导致乱码的问题，此时用这个方法可以将中文字符转化为URL编码，示例如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib<span class="selector-class">.parse</span> import quote</span><br><span class="line"></span><br><span class="line">keyword = <span class="string">'壁纸'</span></span><br><span class="line">url = <span class="string">'https://www.baidu.com/s?wd='</span> + quote(keyword)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(url)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们声明了一个中文的搜索文字，然后用<code>quote()</code>方法对其进行URL编码，最后得到的结果如下：</p>
                  <figure class="highlight llvm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">https://www.baidu.com/s?wd=<span class="symbol">%E5</span><span class="symbol">%A3</span><span class="symbol">%81</span><span class="symbol">%E7</span><span class="symbol">%BA</span><span class="symbol">%B8</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="10-unquote"><a href="#10-unquote" class="headerlink" title="10. unquote()"></a>10. <code>unquote()</code></h2>
                  <p>有了<code>quote()</code>方法，当然还有<code>unquote()</code>方法，它可以进行URL解码，示例如下：</p>
                  <figure class="highlight llvm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from urllib.parse import unquote</span><br><span class="line"></span><br><span class="line">url = 'https://www.baidu.com/s?wd=<span class="symbol">%E5</span><span class="symbol">%A3</span><span class="symbol">%81</span><span class="symbol">%E7</span><span class="symbol">%BA</span><span class="symbol">%B8</span>'</span><br><span class="line">print(unquote(url))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这是上面得到的URL编码后的结果，这里利用<code>unquote()</code>方法还原，结果如下：</p>
                  <figure class="highlight dts">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">https:</span><span class="comment">//www.baidu.com/s?wd=壁纸</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，利用<code>unquote()</code>方法可以方便地实现解码。</p>
                  <p>本节中，我们介绍了<code>parse</code>模块的一些常用URL处理方法。有了这些方法，我们可以方便地实现URL的解析和构造，建议熟练掌握。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-27 14:04:07" itemprop="dateCreated datePublished" datetime="2018-01-27T14:04:07+08:00">2018-01-27</time>
                </span>
                <span id="/5508.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 3.1.3-解析链接" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>6.7k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>6 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5505.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5505.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 3.1.2-处理异常</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>前一节我们了解了请求的发送过程，但是在网络不好的情况下，如果出现了异常，该怎么办呢？这时如果不处理这些异常，程序很可能因报错而终止运行，所以异常处理还是十分有必要的。</p>
                  <p>urllib的<code>error</code>模块定义了由<code>request</code>模块产生的异常。如果出现了问题，<code>request</code>模块便会抛出<code>error</code>模块中定义的异常。</p>
                  <h2 id="1-URLError"><a href="#1-URLError" class="headerlink" title="1. URLError"></a>1. <code>URLError</code></h2>
                  <p>URLError类来自urllib库的error模块，它继承自OSError类，是error异常模块的基类，由request模块生的异常都可以通过捕获这个类来处理。</p>
                  <p>它具有一个属性reason，即返回错误的原因。</p>
                  <p>下面用一个实例来看一下：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib <span class="keyword">import</span> request, error</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    response = request.urlopen(<span class="string">'http://cuiqingcai.com/index.htm'</span>)</span><br><span class="line"><span class="keyword">except</span> error.URLError <span class="keyword">as</span> e:</span><br><span class="line">    print(e.reason)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>我们打开一个不存在的页面，照理来说应该会报错，但是这时我们捕获了URLError这个异常，运行结果如下：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">Not Found</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>程序没有直接报错，而是输出了如上内容，这样通过如上操作，我们就可以避免程序异常终止，同时异常得到了有效处理。</p>
                  <h2 id="2-HTTPError"><a href="#2-HTTPError" class="headerlink" title="2. HTTPError"></a>2. <code>HTTPError</code></h2>
                  <p>它是<code>URLError</code>的子类，专门用来处理HTTP请求错误，比如认证请求失败等。它有如下3个属性。</p>
                  <ul>
                    <li><strong><code>code</code></strong>：返回HTTP状态码，比如404表示网页不存在，500表示服务器内部错误等。</li>
                    <li><strong><code>reason</code></strong>：同父类一样，用于返回错误的原因。</li>
                    <li><strong><code>headers</code></strong>：返回请求头。</li>
                  </ul>
                  <p>下面我们用几个实例来看看：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib import request,error</span><br><span class="line">try:</span><br><span class="line">    response = request.urlopen(<span class="string">'http://cuiqingcai.com/index.htm'</span>)</span><br><span class="line">except error.HTTPError as e:</span><br><span class="line">    <span class="builtin-name">print</span>(e.reason, e.code, e.headers, <span class="attribute">sep</span>=<span class="string">'\n'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="string">Not</span> <span class="string">Found</span></span><br><span class="line"><span class="number">404</span></span><br><span class="line"><span class="attr">Server:</span> <span class="string">nginx/1.4.6</span> <span class="string">(Ubuntu)</span></span><br><span class="line"><span class="attr">Date:</span> <span class="string">Wed,</span> <span class="number">03</span> <span class="string">Aug</span> <span class="number">2016</span> <span class="number">08</span><span class="string">:54:22</span> <span class="string">GMT</span></span><br><span class="line"><span class="attr">Content-Type:</span> <span class="string">text/html;</span> <span class="string">charset=UTF-8</span></span><br><span class="line"><span class="attr">Transfer-Encoding:</span> <span class="string">chunked</span></span><br><span class="line"><span class="attr">Connection:</span> <span class="string">close</span></span><br><span class="line"><span class="attr">X-Powered-By:</span> <span class="string">PHP/5.5.9-1ubuntu4.14</span></span><br><span class="line"><span class="attr">Vary:</span> <span class="string">Cookie</span></span><br><span class="line"><span class="attr">Expires:</span> <span class="string">Wed,</span> <span class="number">11</span> <span class="string">Jan</span> <span class="number">1984</span> <span class="number">05</span><span class="string">:00:00</span> <span class="string">GMT</span></span><br><span class="line"><span class="attr">Cache-Control:</span> <span class="literal">no</span><span class="string">-cache,</span> <span class="string">must-revalidate,</span> <span class="string">max-age=0</span></span><br><span class="line"><span class="attr">Pragma:</span> <span class="literal">no</span><span class="string">-cache</span></span><br><span class="line"><span class="attr">Link:</span> <span class="string">&lt;http://cuiqingcai.com/wp-json/&gt;;</span> <span class="string">rel="https://api.w.org/"</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>依然是同样的网址，这里捕获了<code>HTTPError</code>异常，输出了<code>reason</code>、<code>code</code>和<code>headers</code>属性。</p>
                  <p>因为<code>URLError</code>是<code>HTTPError</code>的父类，所以可以先选择捕获子类的错误，再去捕获父类的错误，所以上述代码更好的写法如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib import request, error</span><br><span class="line"></span><br><span class="line">try:</span><br><span class="line">    response = request.urlopen(<span class="string">'http://cuiqingcai.com/index.htm'</span>)</span><br><span class="line">except error.HTTPError as e:</span><br><span class="line">    <span class="builtin-name">print</span>(e.reason, e.code, e.headers, <span class="attribute">sep</span>=<span class="string">'\n'</span>)</span><br><span class="line">except error.URLError as e:</span><br><span class="line">    <span class="builtin-name">print</span>(e.reason)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="builtin-name">print</span>(<span class="string">'Request Successfully'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样就可以做到先捕获<code>HTTPError</code>，获取它的错误状态码、原因、<code>headers</code>等信息。如果不是<code>HTTPError</code>异常，就会捕获<code>URLError</code>异常，输出错误原因。最后，用<code>else</code>来处理正常的逻辑。这是一个较好的异常处理写法。</p>
                  <p>有时候，<code>reason</code>属性返回的不一定是字符串，也可能是一个对象。再看下面的实例：</p>
                  <figure class="highlight swift">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> urllib.request</span><br><span class="line"><span class="keyword">import</span> urllib.error</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    response = urllib.request.urlopen('https:<span class="comment">//www.baidu.com', timeout=0.01)</span></span><br><span class="line">except urllib.error.<span class="type">URLError</span> <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(type(e.reason))</span><br><span class="line">    <span class="keyword">if</span> isinstance(e.reason, socket.timeout):</span><br><span class="line">        <span class="built_in">print</span>('<span class="type">TIME</span> <span class="type">OUT'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们直接设置超时时间来强制抛出<code>timeout</code>异常。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;<span class="keyword">class</span> <span class="string">'socket.timeout'</span>&gt;</span><br><span class="line"><span class="type">TIME</span> <span class="keyword">OUT</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，<code>reason</code>属性的结果是<code>socket.timeout</code>类。所以，这里我们可以用<code>isinstance()</code>方法来判断它的类型，作出更详细的异常判断。</p>
                  <p>本节中，我们讲述了<code>error</code>模块的相关用法，通过合理地捕获异常可以做出更准确的异常判断，使程序更加稳健。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-27 14:00:58" itemprop="dateCreated datePublished" datetime="2018-01-27T14:00:58+08:00">2018-01-27</time>
                </span>
                <span id="/5505.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 3.1.2-处理异常" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5500.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5500.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 3.1.1-发送请求</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>使用urllib的<code>request</code>模块，我们可以方便地实现请求的发送并得到响应，本节就来看下它的具体用法。</p>
                  <h2 id="1-urlopen"><a href="#1-urlopen" class="headerlink" title="1. urlopen()"></a>1. <code>urlopen()</code></h2>
                  <p><code>urllib.request</code>模块提供了最基本的构造HTTP请求的方法，利用它可以模拟浏览器的一个请求发起过程，同时它还带有处理授权验证（authenticaton）、重定向（redirection)、浏览器Cookies以及其他内容。</p>
                  <p>下面我们来看一下它的强大之处。这里以Python官网为例，我们来把这个网页抓下来：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(<span class="string">'https://www.python.org'</span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.read()</span></span>.decode(<span class="string">'utf-8'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如图3-1所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/3-1.png" alt="">图3-1 运行结果</p>
                  <p>这里我们只用了两行代码，便完成了Python官网的抓取，输出了网页的源代码。得到源代码之后呢？我们想要的链接、图片地址、文本信息不就都可以提取出来了吗？</p>
                  <p>接下来，看看它返回的到底是什么。利用<code>type()</code>方法输出响应的类型：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(<span class="string">'https://www.python.org'</span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(type(response)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出结果如下：</p>
                  <figure class="highlight ceylon">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;<span class="keyword">class</span> <span class="string">'http.client.HTTPResponse'</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，它是一个<code>HTTPResposne</code>类型的对象。它主要包含<code>read()</code>、<code>readinto()</code>、<code>getheader(name)</code>、<code>getheaders()</code>、<code>fileno()</code>等方法，以及<code>msg</code>、<code>version</code>、<code>status</code>、<code>reason</code>、<code>debuglevel</code>、<code>closed</code>等属性。</p>
                  <p>得到这个对象之后，我们把它赋值为<code>response</code>变量，然后就可以调用这些方法和属性，得到返回结果的一系列信息了。</p>
                  <p>例如，调用<code>read()</code>方法可以得到返回的网页内容，调用<code>status</code>属性可以得到返回结果的状态码，如200代表请求成功，404代表网页未找到等。</p>
                  <p>下面再通过一个实例来看看：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(<span class="string">'https://www.python.org'</span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.status)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.getheaders()</span></span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.getheader(<span class="string">'Server'</span>)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight 1c">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="number">200</span></span><br><span class="line">[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('X-Clacks-Overhead', 'GNU Terry Pratchett'), ('Content-Length', '<span class="number">4739</span>7'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 01 Aug <span class="number">2016</span> 09:57:31 GMT'), ('Via', '1.1 varnish'), ('Age', '<span class="number">2473</span>'), ('Connection', 'close'), ('X-Served-By', 'cache-lcy<span class="number">1125</span>-LCY'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '23'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=<span class="number">63072000</span>; includeSubDomains')]</span><br><span class="line">nginx</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可见，前两个输出分别输出了响应的状态码和响应的头信息，最后一个输出通过调用<code>getheader()</code>方法并传递一个参数<code>Server</code>获取了响应头中的<code>Server</code>值，结果是<code>nginx</code>，意思是服务器是用Nginx搭建的。</p>
                  <p>利用最基本的<code>urlopen()</code>方法，可以完成最基本的简单网页的GET请求抓取。</p>
                  <p>如果想给链接传递一些参数，该怎么实现呢？首先看一下<code>urlopen()</code>函数的API：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">urllib.request.urlopen(url, <span class="attribute">data</span>=None, [timeout, ]*, <span class="attribute">cafile</span>=None, <span class="attribute">capath</span>=None, <span class="attribute">cadefault</span>=<span class="literal">False</span>, <span class="attribute">context</span>=None)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，除了第一个参数可以传递URL之外，我们还可以传递其他内容，比如<code>data</code>（附加数据）、<code>timeout</code>（超时时间）等。</p>
                  <p>下面我们详细说明下这几个参数的用法。</p>
                  <h3 id="data参数"><a href="#data参数" class="headerlink" title="data参数"></a><code>data</code>参数</h3>
                  <p><code>data</code>参数是可选的。如果要添加该参数，并且如果它是字节流编码格式的内容，即<code>bytes</code>类型，则需要通过<code>bytes()</code>方法转化。另外，如果传递了这个参数，则它的请求方式就不再是GET方式，而是POST方式。</p>
                  <p>下面用实例来看一下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.parse</span><br><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">data = bytes(urllib<span class="selector-class">.parse</span>.urlencode(&#123;<span class="string">'word'</span>: <span class="string">'hello'</span>&#125;), encoding=<span class="string">'utf8'</span>)</span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(<span class="string">'http://httpbin.org/post'</span>, data=data)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.read()</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们传递了一个参数<code>word</code>，值是<code>hello</code>。它需要被转码成<code>bytes</code>（字节流）类型。其中转字节流采用了<code>bytes()</code>方法，该方法的第一个参数需要是<code>str</code>（字符串）类型，需要用<code>urllib.parse</code>模块里的<code>urlencode()</code>方法来将参数字典转化为字符串；第二个参数指定编码格式，这里指定为<code>utf8</code>。</p>
                  <p>这里请求的站点是httpbin.org，它可以提供HTTP请求测试。本次我们请求的URL为<a href="http://httpbin.org/post" target="_blank" rel="noopener">http://httpbin.org/post</a>，这个链接可以用来测试POST请求，它可以输出请求的一些信息，其中包含我们传递的<code>data</code>参数。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;</span><br><span class="line">     <span class="attr">"args"</span>: &#123;&#125;,</span><br><span class="line">     <span class="attr">"data"</span>: <span class="string">""</span>,</span><br><span class="line">     <span class="attr">"files"</span>: &#123;&#125;,</span><br><span class="line">     <span class="attr">"form"</span>: &#123;</span><br><span class="line">         <span class="attr">"word"</span>: <span class="string">"hello"</span></span><br><span class="line">     &#125;,</span><br><span class="line">     <span class="attr">"headers"</span>: &#123;</span><br><span class="line">         <span class="attr">"Accept-Encoding"</span>: <span class="string">"identity"</span>,</span><br><span class="line">         <span class="attr">"Content-Length"</span>: <span class="string">"10"</span>,</span><br><span class="line">         <span class="attr">"Content-Type"</span>: <span class="string">"application/x-www-form-urlencoded"</span>,</span><br><span class="line">         <span class="attr">"Host"</span>: <span class="string">"httpbin.org"</span>,</span><br><span class="line">         <span class="attr">"User-Agent"</span>: <span class="string">"Python-urllib/3.5"</span></span><br><span class="line">     &#125;,</span><br><span class="line">     <span class="attr">"json"</span>: <span class="literal">null</span>,</span><br><span class="line">     <span class="attr">"origin"</span>: <span class="string">"123.124.23.253"</span>,</span><br><span class="line">     <span class="attr">"url"</span>: <span class="string">"http://httpbin.org/post"</span></span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>我们传递的参数出现在了<code>form</code>字段中，这表明是模拟了表单提交的方式，以POST方式传输数据。</p>
                  <h3 id="timeout参数"><a href="#timeout参数" class="headerlink" title="timeout参数"></a><code>timeout</code>参数</h3>
                  <p><code>timeout</code>参数用于设置超时时间，单位为秒，意思就是如果请求超出了设置的这个时间，还没有得到响应，就会抛出异常。如果不指定该参数，就会使用全局默认时间。它支持HTTP、HTTPS、FTP请求。</p>
                  <p>下面用实例来看一下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(<span class="string">'http://httpbin.org/get'</span>, timeout=<span class="number">1</span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.read()</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight sql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">During handling of the above exception, another exception occurred:</span><br><span class="line"></span><br><span class="line">Traceback (most recent <span class="keyword">call</span> <span class="keyword">last</span>): <span class="keyword">File</span> <span class="string">"/var/py/python/urllibtest.py"</span>, line <span class="number">4</span>, <span class="keyword">in</span> &lt;<span class="keyword">module</span>&gt; response = urllib.request.urlopen(<span class="string">'http://httpbin.org/get'</span>, <span class="keyword">timeout</span>=<span class="number">1</span>)</span><br><span class="line">...</span><br><span class="line">urllib.error.URLError: &lt;urlopen <span class="keyword">error</span> timed <span class="keyword">out</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们设置超时时间是1秒。程序1秒过后，服务器依然没有响应，于是抛出了<code>URLError</code>异常。该异常属于<code>urllib.error</code>模块，错误原因是超时。</p>
                  <p>因此，可以通过设置这个超时时间来控制一个网页如果长时间未响应，就跳过它的抓取。这可以利用<code>try except</code>语句来实现，相关代码如下：</p>
                  <figure class="highlight swift">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> urllib.request</span><br><span class="line"><span class="keyword">import</span> urllib.error</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    response = urllib.request.urlopen('http:<span class="comment">//httpbin.org/get', timeout=0.1)</span></span><br><span class="line">except urllib.error.<span class="type">URLError</span> <span class="keyword">as</span> e:</span><br><span class="line">    <span class="keyword">if</span> isinstance(e.reason, socket.timeout):</span><br><span class="line">        <span class="built_in">print</span>('<span class="type">TIME</span> <span class="type">OUT'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们请求了<a href="http://httpbin.org/get" target="_blank" rel="noopener">http://httpbin.org/get</a>测试链接，设置超时时间是0.1秒，然后捕获了<code>URLError</code>异常，接着判断异常是<code>socket.timeout</code>类型（意思就是超时异常），从而得出它确实是因为超时而报错，打印输出了<code>TIME OUT</code>。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="type">TIME</span> <span class="keyword">OUT</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>按照常理来说，0.1秒内基本不可能得到服务器响应，因此输出了<code>TIME OUT</code>的提示。</p>
                  <p>通过设置<code>timeout</code>这个参数来实现超时处理，有时还是很有用的。</p>
                  <h3 id="其他参数"><a href="#其他参数" class="headerlink" title="其他参数"></a>其他参数</h3>
                  <p>除了<code>data</code>参数和<code>timeout</code>参数外，还有<code>context</code>参数，它必须是<code>ssl.SSLContext</code>类型，用来指定SSL设置。</p>
                  <p>此外，<code>cafile</code>和<code>capath</code>这两个参数分别指定CA证书和它的路径，这个在请求HTTPS链接时会有用。</p>
                  <p><code>cadefault</code>参数现在已经弃用了，其默认值为<code>False</code>。</p>
                  <p>前面讲解了<code>urlopen()</code>方法的用法，通过这个最基本的方法，我们可以完成简单的请求和网页抓取。若需更加详细的信息，可以参见官方文档：<a href="https://docs.python.org/3/library/urllib.request.html" target="_blank" rel="noopener">https://docs.python.org/3/library/urllib.request.html</a>。</p>
                  <h2 id="2-Request"><a href="#2-Request" class="headerlink" title="2. Request"></a>2. <code>Request</code></h2>
                  <p>我们知道利用<code>urlopen()</code>方法可以实现最基本请求的发起，但这几个简单的参数并不足以构建一个完整的请求。如果请求中需要加入Headers等信息，就可以利用更强大的<code>Request</code>类来构建。</p>
                  <p>首先，我们用实例来感受一下<code>Request</code>的用法：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import urllib.request</span><br><span class="line"></span><br><span class="line">request = urllib<span class="selector-class">.request</span>.Request(<span class="string">'https://python.org'</span>)</span><br><span class="line">response = urllib<span class="selector-class">.request</span>.urlopen(request)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(response.read()</span></span>.decode(<span class="string">'utf-8'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现，我们依然是用<code>urlopen()</code>方法来发送这个请求，只不过这次该方法的参数不再是URL，而是一个<code>Request</code>类型的对象。通过构造这个数据结构，一方面我们可以将请求独立成一个对象，另一方面可更加丰富和灵活地配置参数。</p>
                  <p>下面我们看一下<code>Request</code>可以通过怎样的参数来构造，它的构造方法如下：</p>
                  <figure class="highlight oxygene">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">class</span> urllib.request.Request(url, data=None, headers=<span class="comment">&#123;&#125;</span>, origin_req_host=None, unverifiable=<span class="keyword">False</span>, <span class="function"><span class="keyword">method</span>=<span class="title">None</span>)</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <ul>
                    <li>第一个参数<code>url</code>用于请求URL，这是必传参数，其他都是可选参数。</li>
                    <li>第二个参数<code>data</code>如果要传，必须传<code>bytes</code>（字节流）类型的。如果它是字典，可以先用<code>urllib.parse</code>模块里的<code>urlencode()</code>编码。</li>
                    <li>
                      <p>第三个参数<code>headers</code>是一个字典，它就是请求头，我们可以在构造请求时通过<code>headers</code>参数直接构造，也可以通过调用请求实例的<code>add_header()</code>方法添加。 添加请求头最常用的用法就是通过修改<code>User-Agent</code>来伪装浏览器，默认的<code>User-Agent</code>是Python-urllib，我们可以通过修改它来伪装浏览器。比如要伪装火狐浏览器，你可以把它设置为：</p>
                      <figure class="highlight angelscript">
                        <table>
                          <tr>
                            <td class="gutter">
                              <pre><span class="line">1</span><br></pre>
                            </td>
                            <td class="code">
                              <pre><span class="line">Mozilla/<span class="number">5.0</span> (X11; U; Linux i686) Gecko/<span class="number">20071127</span> Firefox/<span class="number">2.0</span><span class="number">.0</span><span class="number">.11</span></span><br></pre>
                            </td>
                          </tr>
                        </table>
                      </figure>
                    </li>
                    <li>
                      <p>第四个参数<code>origin_req_host</code>指的是请求方的host名称或者IP地址。</p>
                    </li>
                    <li>第五个参数<code>unverifiable</code>表示这个请求是否是无法验证的，默认是<code>False</code>，意思就是说用户没有足够权限来选择接收这个请求的结果。例如，我们请求一个HTML文档中的图片，但是我们没有自动抓取图像的权限，这时unverifiable<code>的值就是</code>True`。</li>
                    <li>第六个参数<code>method</code>是一个字符串，用来指示请求使用的方法，比如GET、POST和PUT等。</li>
                  </ul>
                  <p>下面我们传入多个参数构建请求来看一下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib import request, parse</span><br><span class="line"></span><br><span class="line">url = <span class="string">'http://httpbin.org/post'</span></span><br><span class="line">headers = &#123;</span><br><span class="line">    <span class="string">'User-Agent'</span>: <span class="string">'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'</span>,</span><br><span class="line">    <span class="string">'Host'</span>: <span class="string">'httpbin.org'</span></span><br><span class="line">&#125;</span><br><span class="line">dict = &#123;</span><br><span class="line">    <span class="string">'name'</span>: <span class="string">'Germey'</span></span><br><span class="line">&#125;</span><br><span class="line">data = bytes(parse.urlencode(dict), <span class="attribute">encoding</span>=<span class="string">'utf8'</span>)</span><br><span class="line">req = request.Request(<span class="attribute">url</span>=url, <span class="attribute">data</span>=data, <span class="attribute">headers</span>=headers, <span class="attribute">method</span>=<span class="string">'POST'</span>)</span><br><span class="line">response = request.urlopen(req)</span><br><span class="line"><span class="builtin-name">print</span>(response.read().decode(<span class="string">'utf-8'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们通过4个参数构造了一个请求，其中<code>url</code>即请求URL，<code>headers</code>中指定了<code>User-Agent</code>和<code>Host</code>，参数<code>data</code>用<code>urlencode()</code>和<code>bytes()</code>方法转成字节流。另外，指定了请求方式为POST。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"args"</span>: &#123;&#125;, </span><br><span class="line">  <span class="attr">"data"</span>: <span class="string">""</span>, </span><br><span class="line">  <span class="attr">"files"</span>: &#123;&#125;, </span><br><span class="line">  <span class="attr">"form"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"Germey"</span></span><br><span class="line">  &#125;, </span><br><span class="line">  <span class="attr">"headers"</span>: &#123;</span><br><span class="line">    <span class="attr">"Accept-Encoding"</span>: <span class="string">"identity"</span>, </span><br><span class="line">    <span class="attr">"Content-Length"</span>: <span class="string">"11"</span>, </span><br><span class="line">    <span class="attr">"Content-Type"</span>: <span class="string">"application/x-www-form-urlencoded"</span>, </span><br><span class="line">    <span class="attr">"Host"</span>: <span class="string">"httpbin.org"</span>, </span><br><span class="line">    <span class="attr">"User-Agent"</span>: <span class="string">"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"</span></span><br><span class="line">  &#125;, </span><br><span class="line">  <span class="attr">"json"</span>: <span class="literal">null</span>, </span><br><span class="line">  <span class="attr">"origin"</span>: <span class="string">"219.224.169.11"</span>, </span><br><span class="line">  <span class="attr">"url"</span>: <span class="string">"http://httpbin.org/post"</span></span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>观察结果可以发现，我们成功设置了<code>data</code>、<code>headers</code>和<code>method</code>。</p>
                  <p>另外，<code>headers</code>也可以用<code>add_header()</code>方法来添加：</p>
                  <figure class="highlight oxygene">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">req = request.Request(url=url, data=data, <span class="function"><span class="keyword">method</span>='<span class="title">POST</span>')</span></span><br><span class="line"><span class="function"><span class="title">req</span>.<span class="title">add_header</span><span class="params">(<span class="string">'User-Agent'</span>, <span class="string">'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'</span>)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如此一来，我们就可以更加方便地构造请求，实现请求的发送啦。</p>
                  <h2 id="3-高级用法"><a href="#3-高级用法" class="headerlink" title="3. 高级用法"></a>3. 高级用法</h2>
                  <p>在上面的过程中，我们虽然可以构造请求，但是对于一些更高级的操作（比如Cookies处理、代理设置等），我们该怎么办呢？</p>
                  <p>接下来，就需要更强大的工具Handler登场了。简而言之，我们可以把它理解为各种处理器，有专门处理登录验证的，有处理Cookies的，有处理代理设置的。利用它们，我们几乎可以做到HTTP请求中所有的事情。</p>
                  <p>首先，介绍一下<code>urllib.request</code>模块里的<code>BaseHandler</code>类，它是所有其他<code>Handler</code>的父类，它提供了最基本的方法，例如<code>default_open()</code>、<code>protocol_request()</code>等。</p>
                  <p>接下来，就有各种<code>Handler</code>子类继承这个<code>BaseHandler</code>类，举例如下。</p>
                  <ul>
                    <li><strong><code>HTTPDefaultErrorHandler</code></strong>：用于处理HTTP响应错误，错误都会抛出<code>HTTPError</code>类型的异常。</li>
                    <li><strong><code>HTTPRedirectHandler</code></strong>：用于处理重定向。</li>
                    <li><strong><code>HTTPCookieProcessor</code></strong>：用于处理Cookies。</li>
                    <li><strong><code>ProxyHandler</code></strong>：用于设置代理，默认代理为空。</li>
                    <li><strong><code>HTTPPasswordMgr</code></strong>：用于管理密码，它维护了用户名和密码的表。</li>
                    <li><strong><code>HTTPBasicAuthHandler</code></strong>：用于管理认证，如果一个链接打开时需要认证，那么可以用它来解决认证问题。</li>
                  </ul>
                  <p>另外，还有其他的<code>Handler</code>类，这里就不一一列举了，详情可以参考官方文档：<a href="https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler" target="_blank" rel="noopener">https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler</a>。</p>
                  <p>关于怎么使用它们，现在先不用着急，后面会有实例演示。</p>
                  <p>另一个比较重要的类就是<code>OpenerDirector</code>，我们可以称为<code>Opener</code>。我们之前用过<code>urlopen()</code>这个方法，实际上它就是urllib为我们提供的一个<code>Opener</code>。</p>
                  <p>那么，为什么要引入<code>Opener</code>呢？因为需要实现更高级的功能。之前使用的<code>Request</code>和<code>urlopen()</code>相当于类库为你封装好了极其常用的请求方法，利用它们可以完成基本的请求，但是现在不一样了，我们需要实现更高级的功能，所以需要深入一层进行配置，使用更底层的实例来完成操作，所以这里就用到了<code>Opener</code>。</p>
                  <p><code>Opener</code>可以使用<code>open()</code>方法，返回的类型和<code>urlopen()</code>如出一辙。那么，它和<code>Handler</code>有什么关系呢？简而言之，就是利用<code>Handler</code>来构建<code>Opener</code>。</p>
                  <p>下面用几个实例来看看它们的用法。</p>
                  <h3 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h3>
                  <p>有些网站在打开时就会弹出提示框，直接提示你输入用户名和密码，验证成功后才能查看页面，如图3-2所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/3-2.jpg" alt="">图3-2 验证页面</p>
                  <p>那么，如果要请求这样的页面，该怎么办呢？借助<code>HTTPBasicAuthHandler</code>就可以完成，相关代码如下：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.request <span class="keyword">import</span> HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener</span><br><span class="line"><span class="keyword">from</span> urllib.error <span class="keyword">import</span> URLError</span><br><span class="line"></span><br><span class="line">username = <span class="string">'username'</span></span><br><span class="line">password = <span class="string">'password'</span></span><br><span class="line">url = <span class="string">'http://localhost:5000/'</span></span><br><span class="line"></span><br><span class="line">p = HTTPPasswordMgrWithDefaultRealm()</span><br><span class="line">p.add_password(<span class="literal">None</span>, url, username, password)</span><br><span class="line">auth_handler = HTTPBasicAuthHandler(p)</span><br><span class="line">opener = build_opener(auth_handler)</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    result = opener.open(url)</span><br><span class="line">    html = result.read().decode(<span class="string">'utf-8'</span>)</span><br><span class="line">    print(html)</span><br><span class="line"><span class="keyword">except</span> URLError <span class="keyword">as</span> e:</span><br><span class="line">    print(e.reason)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里首先实例化<code>HTTPBasicAuthHandler</code>对象，其参数是<code>HTTPPasswordMgrWithDefaultRealm</code>对象，它利用<code>add_password()</code>添加进去用户名和密码，这样就建立了一个处理验证的<code>Handler</code>。</p>
                  <p>接下来，利用这个<code>Handler</code>并使用<code>build_opener()</code>方法构建一个<code>Opener</code>，这个<code>Opener</code>在发送请求时就相当于已经验证成功了。</p>
                  <p>接下来，利用<code>Opener</code>的<code>open()</code>方法打开链接，就可以完成验证了。这里获取到的结果就是验证后的页面源码内容。</p>
                  <h3 id="代理"><a href="#代理" class="headerlink" title="代理"></a>代理</h3>
                  <p>在做爬虫的时候，免不了要使用代理，如果要添加代理，可以这样做：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> urllib.error <span class="keyword">import</span> URLError</span><br><span class="line"><span class="keyword">from</span> urllib.request <span class="keyword">import</span> ProxyHandler, build_opener</span><br><span class="line"></span><br><span class="line">proxy_handler = ProxyHandler(&#123;</span><br><span class="line">    <span class="string">'http'</span>: <span class="string">'http://127.0.0.1:9743'</span>,</span><br><span class="line">    <span class="string">'https'</span>: <span class="string">'https://127.0.0.1:9743'</span></span><br><span class="line">&#125;)</span><br><span class="line">opener = build_opener(proxy_handler)</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    response = opener.open(<span class="string">'https://www.baidu.com'</span>)</span><br><span class="line">    print(response.read().decode(<span class="string">'utf-8'</span>))</span><br><span class="line"><span class="keyword">except</span> URLError <span class="keyword">as</span> e:</span><br><span class="line">    print(e.reason)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们在本地搭建了一个代理，它运行在9743端口上。</p>
                  <p>这里使用了<code>ProxyHandler</code>，其参数是一个字典，键名是协议类型（比如HTTP或者HTTPS等），键值是代理链接，可以添加多个代理。</p>
                  <p>然后，利用这个Handler及<code>build_opener()</code>方法构造一个<code>Opener</code>，之后发送请求即可。</p>
                  <h3 id="Cookies"><a href="#Cookies" class="headerlink" title="Cookies"></a>Cookies</h3>
                  <p>Cookies的处理就需要相关的<code>Handler</code>了。</p>
                  <p>我们先用实例来看看怎样将网站的Cookies获取下来，相关代码如下：</p>
                  <figure class="highlight livecodeserver">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import <span class="keyword">http</span>.cookiejar, urllib.request</span><br><span class="line"></span><br><span class="line">cookie = <span class="keyword">http</span>.cookiejar.CookieJar()</span><br><span class="line">handler = urllib.request.HTTPCookieProcessor(cookie)</span><br><span class="line">opener = urllib.request.build_opener(handler)</span><br><span class="line">response = opener.<span class="built_in">open</span>(<span class="string">'http://www.baidu.com'</span>)</span><br><span class="line"><span class="keyword">for</span> <span class="keyword">item</span> <span class="keyword">in</span> cookie:</span><br><span class="line">    print(<span class="keyword">item</span>.name+<span class="string">"="</span>+<span class="keyword">item</span>.<span class="built_in">value</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>首先，我们必须声明一个<code>CookieJar</code>对象。接下来，就需要利用<code>HTTPCookieProcessor</code>来构建一个<code>Handler</code>，最后利用<code>build_opener()</code>方法构建出<code>Opener</code>，执行<code>open()</code>函数即可。</p>
                  <p>运行结果如下：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">BAIDUID</span>=<span class="number">2</span>E65A683F8A8BA3DF521469DF8EFF1E1:FG=<span class="number">1</span></span><br><span class="line"><span class="attr">BIDUPSID</span>=<span class="number">2</span>E65A683F8A8BA3DF521469DF8EFF1E1</span><br><span class="line"><span class="attr">H_PS_PSSID</span>=<span class="number">20987_1421_18282_17949_21122_17001_21227_21189_21161_20927</span></span><br><span class="line"><span class="attr">PSTM</span>=<span class="number">1474900615</span></span><br><span class="line"><span class="attr">BDSVRTM</span>=<span class="number">0</span></span><br><span class="line"><span class="attr">BD_HOME</span>=<span class="number">0</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，这里输出了每条Cookie的名称和值。</p>
                  <p>不过既然能输出，那可不可以输出成文件格式呢？我们知道Cookies实际上也是以文本形式保存的。</p>
                  <p>答案当然是肯定的，这里通过下面的实例来看看：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">filename = 'cookies.txt'</span><br><span class="line">cookie = http.cookiejar.<span class="constructor">MozillaCookieJar(<span class="params">filename</span>)</span></span><br><span class="line">handler = urllib.request.<span class="constructor">HTTPCookieProcessor(<span class="params">cookie</span>)</span></span><br><span class="line">opener = urllib.request.build<span class="constructor">_opener(<span class="params">handler</span>)</span></span><br><span class="line">response = opener.<span class="keyword">open</span>('http:<span class="comment">//www.baidu.com')</span></span><br><span class="line">cookie.save(ignore_discard=True, ignore_expires=True)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这时<code>CookieJar</code>就需要换成<code>MozillaCookieJar</code>，它在生成文件时会用到，是<code>CookieJar</code>的子类，可以用来处理Cookies和文件相关的事件，比如读取和保存Cookies，可以将Cookies保存成Mozilla型浏览器的Cookies格式。</p>
                  <p>运行之后，可以发现生成了一个cookies.txt文件，其内容如下：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment"># Netscape HTTP Cookie File</span></span><br><span class="line"><span class="comment"># http://curl.haxx.se/rfc/cookie_spec.html</span></span><br><span class="line"><span class="comment"># This is a generated file!  Do not edit.</span></span><br><span class="line"></span><br><span class="line"><span class="string">.baidu.com</span>    <span class="literal">TRUE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>    <span class="number">3622386254</span>    <span class="string">BAIDUID</span>    <span class="string">05AE39B5F56C1DEC474325CDA522D44F:FG=1</span></span><br><span class="line"><span class="string">.baidu.com</span>    <span class="literal">TRUE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>    <span class="number">3622386254</span>    <span class="string">BIDUPSID</span>    <span class="string">05AE39B5F56C1DEC474325CDA522D44F</span></span><br><span class="line"><span class="string">.baidu.com</span>    <span class="literal">TRUE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>        <span class="string">H_PS_PSSID</span>    <span class="string">19638_1453_17710_18240_21091_18560_17001_21191_21161</span></span><br><span class="line"><span class="string">.baidu.com</span>    <span class="literal">TRUE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>    <span class="number">3622386254</span>    <span class="string">PSTM</span>    <span class="number">1474902606</span></span><br><span class="line"><span class="string">www.baidu.com</span>    <span class="literal">FALSE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>        <span class="string">BDSVRTM</span>    <span class="number">0</span></span><br><span class="line"><span class="string">www.baidu.com</span>    <span class="literal">FALSE</span>    <span class="string">/</span>    <span class="literal">FALSE</span>        <span class="string">BD_HOME</span>    <span class="number">0</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>另外，<code>LWPCookieJar</code>同样可以读取和保存Cookies，但是保存的格式和<code>MozillaCookieJar</code>不一样，它会保存成libwww-perl(LWP)格式的Cookies文件。</p>
                  <p>要保存成LWP格式的Cookies文件，可以在声明时就改为：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">cookie</span> = http.cookiejar.LWPCookieJar(filename)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>此时生成的内容如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment">#LWP-Cookies-2.0</span></span><br><span class="line">Set-Cookie3: <span class="attribute">BAIDUID</span>=<span class="string">"0CE9C56F598E69DB375B7C294AE5C591:FG=1"</span>; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">".baidu.com"</span>; path_spec; domain_dot; <span class="attribute">expires</span>=<span class="string">"2084-10-14 18:25:19Z"</span>; <span class="attribute">version</span>=0</span><br><span class="line">Set-Cookie3: <span class="attribute">BIDUPSID</span>=0CE9C56F598E69DB375B7C294AE5C591; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">".baidu.com"</span>; path_spec; domain_dot; <span class="attribute">expires</span>=<span class="string">"2084-10-14 18:25:19Z"</span>; <span class="attribute">version</span>=0</span><br><span class="line">Set-Cookie3: <span class="attribute">H_PS_PSSID</span>=20048_1448_18240_17944_21089_21192_21161_20929; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">".baidu.com"</span>; path_spec; domain_dot; discard; <span class="attribute">version</span>=0</span><br><span class="line">Set-Cookie3: <span class="attribute">PSTM</span>=1474902671; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">".baidu.com"</span>; path_spec; domain_dot; <span class="attribute">expires</span>=<span class="string">"2084-10-14 18:25:19Z"</span>; <span class="attribute">version</span>=0</span><br><span class="line">Set-Cookie3: <span class="attribute">BDSVRTM</span>=0; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">"www.baidu.com"</span>; path_spec; discard; <span class="attribute">version</span>=0</span><br><span class="line">Set-Cookie3: <span class="attribute">BD_HOME</span>=0; <span class="attribute">path</span>=<span class="string">"/"</span>; <span class="attribute">domain</span>=<span class="string">"www.baidu.com"</span>; path_spec; discard; <span class="attribute">version</span>=0</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>由此看来，生成的格式还是有比较大差异的。</p>
                  <p>那么，生成了Cookies文件后，怎样从文件中读取并利用呢？</p>
                  <p>下面我们以<code>LWPCookieJar</code>格式为例来看一下：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">cookie = http.cookiejar.LWPCookieJar()</span><br><span class="line">cookie.<span class="keyword">load</span>(<span class="string">'cookies.txt'</span>, ignore_discard=<span class="keyword">True</span>, ignore_expires=<span class="keyword">True</span>)</span><br><span class="line"><span class="keyword">handler</span> = urllib.request.HTTPCookieProcessor(cookie)</span><br><span class="line">opener = urllib.request.build_opener(<span class="keyword">handler</span>)</span><br><span class="line">response = opener.<span class="keyword">open</span>(<span class="string">'http://www.baidu.com'</span>)</span><br><span class="line">print(response.<span class="keyword">read</span>().decode(<span class="string">'utf-8'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，这里调用<code>load()</code>方法来读取本地的Cookies文件，获取到了Cookies的内容。不过前提是我们首先生成了LWPCookieJar格式的Cookies，并保存成文件，然后读取Cookies之后使用同样的方法构建Handler和Opener即可完成操作。</p>
                  <p>运行结果正常的话，会输出百度网页的源代码。</p>
                  <p>通过上面的方法，我们可以实现绝大多数请求功能的设置了。</p>
                  <p>这便是urllib库中<code>request</code>模块的基本用法，如果想实现更多的功能，可以参考官方文档的说明：<a href="https://docs.python.org/3/library/urllib.request.html#basehandler-objects" target="_blank" rel="noopener">https://docs.python.org/3/library/urllib.request.html#basehandler-objects</a>。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-27 11:49:34" itemprop="dateCreated datePublished" datetime="2018-01-27T11:49:34+08:00">2018-01-27</time>
                </span>
                <span id="/5500.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 3.1.1-发送请求" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>12k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>11 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5497.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5497.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 3.1-使用urllib</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在Python 2中，有urllib和urllib2两个库来实现请求的发送。而在Python 3中，已经不存在urllib2这个库了，统一为urllib，其官方文档链接为：<a href="https://docs.python.org/3/library/urllib.html" target="_blank" rel="noopener">https://docs.python.org/3/library/urllib.html</a>。</p>
                  <p>首先，了解一下urllib库，它是Python内置的HTTP请求库，也就是说不需要额外安装即可使用。它包含如下4个模块。</p>
                  <ul>
                    <li><strong><code>request</code></strong>：它是最基本的HTTP请求模块，可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样，只需要给库方法传入URL以及额外的参数，就可以模拟实现这个过程了。</li>
                    <li><strong><code>error</code></strong>：异常处理模块，如果出现请求错误，我们可以捕获这些异常，然后进行重试或其他操作以保证程序不会意外终止。</li>
                    <li><strong><code>parse</code></strong>：一个工具模块，提供了许多URL处理方法，比如拆分、解析、合并等。</li>
                    <li><strong><code>robotparser</code></strong>：主要是用来识别网站的robots.txt文件，然后判断哪些网站可以爬，哪些网站不可以爬，它其实用得比较少。</li>
                  </ul>
                  <p>这里重点讲解一下前3个模块。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-27 11:38:28" itemprop="dateCreated datePublished" datetime="2018-01-27T11:38:28+08:00">2018-01-27</time>
                </span>
                <span id="/5497.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 3.1-使用urllib" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>448</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5494.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5494.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 3-基本库的使用</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>学习爬虫，最初的操作便是模拟浏览器向服务器发出请求，那么我们需要从哪个地方做起呢？请求需要我们自己来构造吗？需要关心请求这个数据结构的实现吗？需要了解HTTP、TCP、IP层的网络传输通信吗？需要知道服务器的响应和应答原理吗？</p>
                  <p>可能你不知道无从下手，不过不用担心，Python的强大之处就是提供了功能齐全的类库来帮助我们完成这些请求。最基础的HTTP库有urllib、httplib2、requests、treq等。</p>
                  <p>拿urllib这个库来说，有了它，我们只需要关心请求的链接是什么，需要传的参数是什么以及可选的请求头设置就好了，不用深入到底层去了解它到底是怎样传输和通信的。有了它，两行代码就可以完成一个请求和响应的处理过程，得到网页内容，是不是感觉方便极了？</p>
                  <p>接下来，就让我们从最基础的部分开始了解这些库的使用方法吧。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-27 11:36:36" itemprop="dateCreated datePublished" datetime="2018-01-27T11:36:36+08:00">2018-01-27</time>
                </span>
                <span id="/5494.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 3-基本库的使用" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>358</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5491.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5491.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2.5-代理的基本原理</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>我们在做爬虫的过程中经常会遇到这样的情况，最初爬虫正常运行，正常抓取数据，一切看起来都是那么美好，然而一杯茶的功夫可能就会出现错误，比如403 Forbidden，这时候打开网页一看，可能会看到“您的IP访问频率太高”这样的提示。出现这种现象的原因是网站采取了一些反爬虫措施。比如，服务器会检测某个IP在单位时间内的请求次数，如果超过了这个阈值，就会直接拒绝服务，返回一些错误信息，这种情况可以称为封IP。</p>
                  <p>既然服务器检测的是某个IP单位时间的请求次数，那么借助某种方式来伪装我们的IP，让服务器识别不出是由我们本机发起的请求，不就可以成功防止封IP了吗？</p>
                  <p>一种有效的方式就是使用代理，后面会详细说明代理的用法。在这之前，需要先了解下代理的基本原理，它是怎样实现IP伪装的呢？</p>
                  <h2 id="1-基本原理"><a href="#1-基本原理" class="headerlink" title="1. 基本原理"></a>1. 基本原理</h2>
                  <p>代理实际上指的就是代理服务器，英文叫作proxy server，它的功能是代理网络用户去取得网络信息。形象地说，它是网络信息的中转站。在我们正常请求一个网站时，是发送了请求给Web服务器，Web服务器把响应传回给我们。如果设置了代理服务器，实际上就是在本机和服务器之间搭建了一个桥，此时本机不是直接向Web服务器发起请求，而是向代理服务器发出请求，请求会发送给代理服务器，然后由代理服务器再发送给Web服务器，接着由代理服务器再把Web服务器返回的响应转发给本机。这样我们同样可以正常访问网页，但这个过程中Web服务器识别出的真实IP就不再是我们本机的IP了，就成功实现了IP伪装，这就是代理的基本原理。</p>
                  <h2 id="2-代理的作用"><a href="#2-代理的作用" class="headerlink" title="2. 代理的作用"></a>2. 代理的作用</h2>
                  <p>那么，代理有什么作用呢？我们可以简单列举如下。</p>
                  <ul>
                    <li>突破自身IP访问限制，访问一些平时不能访问的站点。</li>
                    <li>访问一些单位或团体内部资源：比如使用教育网内地址段免费代理服务器，就可以用于对教育网开放的各类FTP下载上传，以及各类资料查询共享等服务。</li>
                    <li>提高访问速度：通常代理服务器都设置一个较大的硬盘缓冲区，当有外界的信息通过时，同时也将其保存到缓冲区中，当其他用户再访问相同的信息时，则直接由缓冲区中取出信息，传给用户，以提高访问速度。</li>
                    <li>隐藏真实IP：上网者也可以通过这种方法隐藏自己的IP，免受攻击。对于爬虫来说，我们用代理就是为了隐藏自身IP，防止自身的IP被封锁。</li>
                  </ul>
                  <h2 id="3-爬虫代理"><a href="#3-爬虫代理" class="headerlink" title="3. 爬虫代理"></a>3. 爬虫代理</h2>
                  <p>对于爬虫来说，由于爬虫爬取速度过快，在爬取过程中可能遇到同一个IP访问过于频繁的问题，此时网站就会让我们输入验证码登录或者直接封锁IP，这样会给爬取带来极大的不便。</p>
                  <p>使用代理隐藏真实的IP，让服务器误以为是代理服务器在请求自己。这样在爬取过程中通过不断更换代理，就不会被封锁，可以达到很好的爬取效果。</p>
                  <h2 id="4-代理分类"><a href="#4-代理分类" class="headerlink" title="4. 代理分类"></a>4. 代理分类</h2>
                  <p>代理分类时，既可以根据协议区分，也可以根据其匿名程度区分。</p>
                  <h3 id="1-根据协议区分"><a href="#1-根据协议区分" class="headerlink" title="(1) 根据协议区分"></a>(1) 根据协议区分</h3>
                  <p>根据代理的协议，代理可以分为如下类别。</p>
                  <ul>
                    <li><strong>FTP代理服务器</strong>：主要用于访问FTP服务器，一般有上传、下载以及缓存功能，端口一般为21、2121等。</li>
                    <li><strong>HTTP代理服务器</strong>：主要用于访问网页，一般有内容过滤和缓存功能，端口一般为80、8080、3128等。</li>
                    <li><strong>SSL/TLS代理</strong>：主要用于访问加密网站，一般有SSL或TLS加密功能（最高支持128位加密强度），端口一般为443。</li>
                    <li><strong>RTSP代理</strong>：主要用于访问Real流媒体服务器，一般有缓存功能，端口一般为554。</li>
                    <li><strong>Telnet代理</strong>：主要用于telnet远程控制（黑客入侵计算机时常用于隐藏身份），端口一般为23。</li>
                    <li><strong>POP3/SMTP代理</strong>：主要用于POP3/SMTP方式收发邮件，一般有缓存功能，端口一般为110/25。</li>
                    <li><strong>SOCKS代理</strong>：只是单纯传递数据包，不关心具体协议和用法，所以速度快很多，一般有缓存功能，端口一般为1080。SOCKS代理协议又分为SOCKS4和SOCKS5，前者只支持TCP，而后者支持TCP和UDP，还支持各种身份验证机制、服务器端域名解析等。简单来说，SOCK4能做到的SOCKS5都可以做到，但SOCKS5能做到的SOCK4不一定能做到。</li>
                  </ul>
                  <h3 id="2-根据匿名程度区分"><a href="#2-根据匿名程度区分" class="headerlink" title="(2) 根据匿名程度区分"></a>(2) 根据匿名程度区分</h3>
                  <p>根据代理的匿名程度，代理可以分为如下类别。</p>
                  <ul>
                    <li><strong>高度匿名代理</strong>：会将数据包原封不动地转发，在服务端看来就好像真的是一个普通客户端在访问，而记录的IP是代理服务器的IP。</li>
                    <li><strong>普通匿名代理</strong>：会在数据包上做一些改动，服务端上有可能发现这是个代理服务器，也有一定几率追查到客户端的真实IP。代理服务器通常会加入的HTTP头有<code>HTTP_VIA</code>和<code>HTTP_X_FORWARDED_FOR</code>。</li>
                    <li><strong>透明代理</strong>：不但改动了数据包，还会告诉服务器客户端的真实IP。这种代理除了能用缓存技术提高浏览速度，能用内容过滤提高安全性之外，并无其他显著作用，最常见的例子是内网中的硬件防火墙。</li>
                    <li><strong>间谍代理</strong>：指组织或个人创建的用于记录用户传输的数据，然后进行研究、监控等目的的代理服务器。</li>
                  </ul>
                  <h2 id="5-常见代理设置"><a href="#5-常见代理设置" class="headerlink" title="5. 常见代理设置"></a>5. 常见代理设置</h2>
                  <ul>
                    <li>使用网上的免费代理：最好使用高匿代理，另外可用的代理不多，需要在使用前筛选一下可用代理，也可以进一步维护一个代理池。</li>
                    <li>使用付费代理服务：互联网上存在许多代理商，可以付费使用，质量比免费代理好很多。</li>
                    <li>ADSL拨号：拨一次号换一次IP，稳定性高，也是一种比较有效的解决方案。</li>
                  </ul>
                  <p>在后文我们会详细介绍这几种代理的使用方式。</p>
                  <h2 id="6-参考来源"><a href="#6-参考来源" class="headerlink" title="6. 参考来源"></a>6. 参考来源</h2>
                  <p>由于涉及一些专业名词知识，本节的部分内容参考来源如下。</p>
                  <ul>
                    <li>代理服务器 维基百科：<a href="http://epub.ituring.com.cn/article/edit/[https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1%E5%99%A8" target="_blank" rel="noopener">https://zh.wikipedia.org/wiki/代理服务器</a></li>
                    <li>代理 百度百科：<a href="https://baike.baidu.com/item/%E4%BB%A3%E7%90%86/3242667" target="_blank" rel="noopener">https://baike.baidu.com/item/代理/3242667</a></li>
                  </ul>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 11:32:42" itemprop="dateCreated datePublished" datetime="2018-01-26T11:32:42+08:00">2018-01-26</time>
                </span>
                <span id="/5491.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2.5-代理的基本原理" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5487.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5487.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2.4-会话和Cookies</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在浏览网站的过程中，我们经常会遇到需要登录的情况，有些页面只有登录之后才可以访问，而且登录之后可以连续访问很多次网站，但是有时候过一段时间就需要重新登录。还有一些网站，在打开浏览器时就自动登录了，而且很长时间都不会失效，这种情况又是为什么？其实这里面涉及会话和Cookies的相关知识，本节就来揭开它们的神秘面纱。</p>
                  <h2 id="1-静态网页和动态网页"><a href="#1-静态网页和动态网页" class="headerlink" title="1. 静态网页和动态网页"></a>1. 静态网页和动态网页</h2>
                  <p>在开始之前，我们需要先了解一下静态网页和动态网页的概念。这里还是前面的示例代码，内容如下：</p>
                  <figure class="highlight xml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">title</span>&gt;</span>This is a Demo<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"wrapper"</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">h2</span> <span class="attr">class</span>=<span class="string">"title"</span>&gt;</span>Hello World<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">p</span> <span class="attr">class</span>=<span class="string">"text"</span>&gt;</span>Hello, this is a paragraph.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这是最基本的HTML代码，我们将其保存为一个.html文件，然后把它放在某台具有固定公网IP的主机上，主机上装上Apache或Nginx等服务器，这样这台主机就可以作为服务器了，其他人便可以通过访问服务器看到这个页面，这就搭建了一个最简单的网站。</p>
                  <p>这种网页的内容是HTML代码编写的，文字、图片等内容均通过写好的HTML代码来指定，这种页面叫作静态网页。它加载速度快，编写简单，但是存在很大的缺陷，如可维护性差，不能根据URL灵活多变地显示内容等。例如，我们想要给这个网页的URL传入一个<code>name</code>参数，让其在网页中显示出来，是无法做到的。</p>
                  <p>因此，动态网页应运而生，它可以动态解析URL中参数的变化，关联数据库并动态呈现不同的页面内容，非常灵活多变。我们现在遇到的大多数网站都是动态网站，它们不再是一个简单的HTML，而是可能由JSP、PHP、Python等语言编写的，其功能比静态网页强大和丰富太多了。</p>
                  <p>此外，动态网站还可以实现用户登录和注册的功能。再回到开头提到的问题，很多页面是需要登录之后才可以查看的。按照一般的逻辑来说，输入用户名和密码登录之后，肯定是拿到了一种类似凭证的东西，有了它，我们才能保持登录状态，才能访问登录之后才能看到的页面。</p>
                  <p>那么，这种神秘的凭证到底是什么呢？其实它就是会话和Cookies共同产生的结果，下面我们来一探究竟。</p>
                  <h2 id="2-无状态HTTP"><a href="#2-无状态HTTP" class="headerlink" title="2. 无状态HTTP"></a>2. 无状态HTTP</h2>
                  <p>在了解会话和Cookies之前，我们还需要了解HTTP的一个特点，叫作无状态。</p>
                  <p>HTTP的无状态是指HTTP协议对事务处理是没有记忆能力的，也就是说服务器不知道客户端是什么状态。当我们向服务器发送请求后，服务器解析此请求，然后返回对应的响应，服务器负责完成这个过程，而且这个过程是完全独立的，服务器不会记录前后状态的变化，也就是缺少状态记录。这意味着如果后续需要处理前面的信息，则必须重传，这导致需要额外传递一些前面的重复请求，才能获取后续响应，然而这种效果显然不是我们想要的。为了保持前后状态，我们肯定不能将前面的请求全部重传一次，这太浪费资源了，对于这种需要用户登录的页面来说，更是棘手。</p>
                  <p>这时两个用于保持HTTP连接状态的技术就出现了，它们分别是会话和Cookies。会话在服务端，也就是网站的服务器，用来保存用户的会话信息；Cookies在客户端，也可以理解为浏览器端，有了Cookies，浏览器在下次访问网页时会自动附带上它发送给服务器，服务器通过识别Cookies并鉴定出是哪个用户，然后再判断用户是否是登录状态，然后返回对应的响应。</p>
                  <p>我们可以理解为Cookies里面保存了登录的凭证，有了它，只需要在下次请求携带Cookies发送请求而不必重新输入用户名、密码等信息重新登录了。</p>
                  <p>因此在爬虫中，有时候处理需要登录才能访问的页面时，我们一般会直接将登录成功后获取的Cookies放在请求头里面直接请求，而不必重新模拟登录。</p>
                  <p>好了，了解会话和Cookies的概念之后，我们在来详细剖析它们的原理。</p>
                  <h3 id="1-会话"><a href="#1-会话" class="headerlink" title="(1) 会话"></a>(1) 会话</h3>
                  <p>会话，其本来的含义是指有始有终的一系列动作/消息。比如，打电话时，从拿起电话拨号到挂断电话这中间的一系列过程可以称为一个会话。</p>
                  <p>而在Web中，会话对象用来存储特定用户会话所需的属性及配置信息。这样，当用户在应用程序的Web页之间跳转时，存储在会话对象中的变量将不会丢失，而是在整个用户会话中一直存在下去。当用户请求来自应用程序的Web页时，如果该用户还没有会话，则Web服务器将自动创建一个会话对象。当会话过期或被放弃后，服务器将终止该会话。</p>
                  <h3 id="2-Cookies"><a href="#2-Cookies" class="headerlink" title="(2) Cookies"></a>(2) Cookies</h3>
                  <p>Cookies指某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。</p>
                  <h4 id="会话维持"><a href="#会话维持" class="headerlink" title="会话维持"></a>会话维持</h4>
                  <p>那么，我们怎样利用Cookies保持状态呢？当客户端第一次请求服务器时，服务器会返回一个请求头中带有<code>Set-Cookie</code>字段的响应给客户端，用来标记是哪一个用户，客户端浏览器会把Cookies保存起来。当浏览器下一次再请求该网站时，浏览器会把此Cookies放到请求头一起提交给服务器，Cookies携带了会话ID信息，服务器检查该Cookies即可找到对应的会话是什么，然后再判断会话来以此来辨认用户状态。</p>
                  <p>在成功登录某个网站时，服务器会告诉客户端设置哪些Cookies信息，在后续访问页面时客户端会把Cookies发送给服务器，服务器再找到对应的会话加以判断。如果会话中的某些设置登录状态的变量是有效的，那就证明用户处于登录状态，此时返回登录之后才可以查看的网页内容，浏览器再进行解析便可以看到了。</p>
                  <p>反之，如果传给服务器的Cookies是无效的，或者会话已经过期了，我们将不能继续访问页面，此时可能会收到错误的响应或者跳转到登录页面重新登录。</p>
                  <p>所以，Cookies和会话需要配合，一个处于客户端，一个处于服务端，二者共同协作，就实现了登录会话控制。</p>
                  <h4 id="属性结构"><a href="#属性结构" class="headerlink" title="属性结构"></a>属性结构</h4>
                  <p>接下来，我们来看看Cookies都有哪些内容。这里以知乎为例，在浏览器开发者工具中打开Application选项卡，然后在左侧会有一个Storage部分，最后一项即为Cookies，将其点开，如图2-13所示，这些就是Cookies。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-14.jpg" alt="">图2-13 Cookies列表</p>
                  <p>可以看到，这里有很多条目，其中每个条目可以称为Cookie。它有如下几个属性。</p>
                  <ul>
                    <li><strong>Name</strong>：该Cookie的名称。一旦创建，该名称便不可更改。</li>
                    <li><strong>Value</strong>：该Cookie的值。如果值为Unicode字符，需要为字符编码。如果值为二进制数据，则需要使用BASE64编码。</li>
                    <li><strong>Domain</strong>：可以访问该Cookie的域名。例如，如果设置为.zhihu.com，则所有以zhihu.com，结尾的域名都可以访问该Cookie。</li>
                    <li><strong>Max Age</strong>：该Cookie失效的时间，单位为秒，也常和Expires一起使用，通过它可以计算出其有效时间。Max Age如果为正数，则该Cookie在Max Age秒之后失效。如果为负数，则关闭浏览器时Cookie即失效，浏览器也不会以任何形式保存该Cookie。</li>
                    <li><strong>Path</strong>：该Cookie的使用路径。如果设置为/path/，则只有路径为/path/的页面可以访问该Cookie。如果设置为/，则本域名下的所有页面都可以访问该Cookie。</li>
                    <li><strong>Size字段</strong>：此Cookie的大小。</li>
                    <li><strong>HTTP字段</strong>：Cookie的<code>httponly</code>属性。若此属性为<code>true</code>，则只有在HTTP头中会带有此Cookie的信息，而不能通过<code>document.cookie</code>来访问此Cookie。</li>
                    <li><strong>Secure</strong>：该Cookie是否仅被使用安全协议传输。安全协议有HTTPS和SSL等，在网络上传输数据之前先将数据加密。默认为<code>false</code>。</li>
                  </ul>
                  <h4 id="会话Cookie和持久Cookie"><a href="#会话Cookie和持久Cookie" class="headerlink" title="会话Cookie和持久Cookie"></a>会话Cookie和持久Cookie</h4>
                  <p>从表面意思来说，会话Cookie就是把Cookie放在浏览器内存里，浏览器在关闭之后该Cookie即失效；持久Cookie则会保存到客户端的硬盘中，下次还可以继续使用，用于长久保持用户登录状态。</p>
                  <p>其实严格来说，没有会话Cookie和持久Cookie之分，只是由Cookie的Max Age或Expires字段决定了过期的时间。</p>
                  <p>因此，一些持久化登录的网站其实就是把Cookie的有效时间和会话有效期设置得比较长，下次我们再访问页面时仍然携带之前的Cookie，就可以直接保持登录状态。</p>
                  <h2 id="3-常见误区"><a href="#3-常见误区" class="headerlink" title="3. 常见误区"></a>3. 常见误区</h2>
                  <p>在谈论会话机制的时候，常常听到这样一种误解“只要关闭浏览器，会话就消失了”，这种理解是错误的。可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对会话来说，也是一样，除非程序通知服务器删除一个会话，否则服务器会一直保留。比如，程序一般都是在我们做注销操作时才去删除会话。</p>
                  <p>但是当我们关闭浏览器时，浏览器不会主动在关闭之前通知服务器它将要关闭，所以服务器根本不会有机会知道浏览器已经关闭。之所以会有这种错觉，是因为大部分会话机制都使用会话Cookie来保存会话ID信息，而关闭浏览器后Cookies就消失了，再次连接服务器时，也就无法找到原来的会话了。如果服务器设置的Cookies保存到硬盘上，或者使用某种手段改写浏览器发出的HTTP请求头，把原来的Cookies发送给服务器，则再次打开浏览器，仍然能够找到原来的会话 ID，依旧还是可以保持登录状态的。</p>
                  <p>而且恰恰是由于关闭浏览器不会导致会话被删除，这就需要服务器为会话设置一个失效时间，当距离客户端上一次使用会话的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把会话删除以节省存储空间。</p>
                  <h2 id="4-参考资料"><a href="#4-参考资料" class="headerlink" title="4. 参考资料"></a>4. 参考资料</h2>
                  <p>由于涉及一些专业名词知识，本节的部分内容参考来源如下。</p>
                  <ul>
                    <li>会话百度百科：<a href="https://baike.baidu.com/item/session/479100" target="_blank" rel="noopener">https://baike.baidu.com/item/session/479100</a></li>
                    <li>Cookies百度百科：<a href="https://baike.baidu.com/item/cookie/1119" target="_blank" rel="noopener">https://baike.baidu.com/item/cookie/1119</a></li>
                    <li>HTTP Cookie维基百科：<a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/HTTP_cookie</a></li>
                    <li>会话和几种状态保持方案理解：<a href="http://www.mamicode.com/info-detail-46545.html" target="_blank" rel="noopener">http://www.mamicode.com/info-detail-46545.html</a></li>
                  </ul>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 11:28:28" itemprop="dateCreated datePublished" datetime="2018-01-26T11:28:28+08:00">2018-01-26</time>
                </span>
                <span id="/5487.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2.4-会话和Cookies" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>4.3k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>4 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5484.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5484.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2.3-爬虫的基本原理</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>我们可以把互联网比作一张大网，而爬虫（即网络爬虫）便是在网上爬行的蜘蛛。把网的节点比作一个个网页，爬虫爬到这就相当于访问了该页面，获取了其信息。可以把节点间的连线比作网页与网页之间的链接关系，这样蜘蛛通过一个节点后，可以顺着节点连线继续爬行到达下一个节点，即通过一个网页继续获取后续的网页，这样整个网的节点便可以被蜘蛛全部爬行到，网站的数据就可以被抓取下来了。</p>
                  <h2 id="1-爬虫概述"><a href="#1-爬虫概述" class="headerlink" title="1. 爬虫概述"></a>1. 爬虫概述</h2>
                  <p>简单来说，爬虫就是获取网页并提取和保存信息的自动化程序，下面概要介绍一下。</p>
                  <h3 id="1-获取网页"><a href="#1-获取网页" class="headerlink" title="(1) 获取网页"></a>(1) 获取网页</h3>
                  <p>爬虫首先要做的工作就是获取网页，这里就是获取网页的源代码。源代码里包含了网页的部分有用信息，所以只要把源代码获取下来，就可以从中提取想要的信息了。</p>
                  <p>前面讲了请求和响应的概念，向网站的服务器发送一个请求，返回的响应体便是网页源代码。所以，最关键的部分就是构造一个请求并发送给服务器，然后接收到响应并将其解析出来，那么这个流程怎样实现呢？总不能手工去截取网页源码吧？</p>
                  <p>不用担心，Python提供了许多库来帮助我们实现这个操作，如urllib、requests等。我们可以用这些库来帮助我们实现HTTP请求操作，请求和响应都可以用类库提供的数据结构来表示，得到响应之后只需要解析数据结构中的Body部分即可，即得到网页的源代码，这样我们可以用程序来实现获取网页的过程了。</p>
                  <h3 id="2-提取信息"><a href="#2-提取信息" class="headerlink" title="(2) 提取信息"></a>(2) 提取信息</h3>
                  <p>获取网页源代码后，接下来就是分析网页源代码，从中提取我们想要的数据。首先，最通用的方法便是采用正则表达式提取，这是一个万能的方法，但是在构造正则表达式时比较复杂且容易出错。</p>
                  <p>另外，由于网页的结构有一定的规则，所以还有一些根据网页节点属性、CSS选择器或XPath来提取网页信息的库，如Beautiful Soup、pyquery、lxml等。使用这些库，我们可以高效快速地从中提取网页信息，如节点的属性、文本值等。</p>
                  <p>提取信息是爬虫非常重要的部分，它可以使杂乱的数据变得条理清晰，以便我们后续处理和分析数据。</p>
                  <h3 id="3-保存数据"><a href="#3-保存数据" class="headerlink" title="(3) 保存数据"></a>(3) 保存数据</h3>
                  <p>提取信息后，我们一般会将提取到的数据保存到某处以便后续使用。这里保存形式有多种多样，如可以简单保存为TXT文本或JSON文本，也可以保存到数据库，如MySQL和MongoDB等，也可保存至远程服务器，如借助SFTP进行操作等。</p>
                  <h3 id="4-自动化程序"><a href="#4-自动化程序" class="headerlink" title="(4) 自动化程序"></a>(4) 自动化程序</h3>
                  <p>说到自动化程序，意思是说爬虫可以代替人来完成这些操作。首先，我们手工当然可以提取这些信息，但是当量特别大或者想快速获取大量数据的话，肯定还是要借助程序。爬虫就是代替我们来完成这份爬取工作的自动化程序，它可以在抓取过程中进行各种异常处理、错误重试等操作，确保爬取持续高效地运行。</p>
                  <h2 id="2-能抓怎样的数据"><a href="#2-能抓怎样的数据" class="headerlink" title="2. 能抓怎样的数据"></a>2. 能抓怎样的数据</h2>
                  <p>在网页中我们能看到各种各样的信息，最常见的便是常规网页，它们对应着HTML代码，而最常抓取的便是HTML源代码。</p>
                  <p>另外，可能有些网页返回的不是HTML代码，而是一个JSON字符串（其中API接口大多采用这样的形式），这种格式的数据方便传输和解析，它们同样可以抓取，而且数据提取更加方便。</p>
                  <p>此外，我们还可以看到各种二进制数据，如图片、视频和音频等。利用爬虫，我们可以将这些二进制数据抓取下来，然后保存成对应的文件名。</p>
                  <p>另外，还可以看到各种扩展名的文件，如CSS、JavaScript和配置文件等，这些其实也是最普通的文件，只要在浏览器里面可以访问到，就可以将其抓取下来。</p>
                  <p>上述内容其实都对应各自的URL，是基于HTTP或HTTPS协议的，只要是这种数据，爬虫都可以抓取。</p>
                  <h2 id="3-JavaScript渲染页面"><a href="#3-JavaScript渲染页面" class="headerlink" title="3. JavaScript渲染页面"></a>3. JavaScript渲染页面</h2>
                  <p>有时候，我们在用urllib或requests抓取网页时，得到的源代码实际和浏览器中看到的不一样。</p>
                  <p>这是一个非常常见的问题。现在网页越来越多地采用Ajax、前端模块化工具来构建，整个网页可能都是由JavaScript渲染出来的，也就是说原始的HTML代码就是一个空壳，例如：</p>
                  <figure class="highlight xml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">title</span>&gt;</span>This is a Demo<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"app.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p><code>body</code>节点里面只有一个<code>id</code>为<code>container</code>的节点，但是需要注意在<code>body</code>节点后引入了app.js，它便负责整个网站的渲染。</p>
                  <p>在浏览器中打开这个页面时，首先会加载这个HTML内容，接着浏览器会发现其中引入了一个app.js文件，然后便会接着去请求这个文件，获取到该文件后，便会执行其中的JavaScript代码，而JavaScript则会改变HTML中的节点，向其添加内容，最后得到完整的页面。</p>
                  <p>但是在用urllib或requests等库请求当前页面时，我们得到的只是这个HTML代码，它不会帮助我们去继续加载这个JavaScript文件，这样也就看不到浏览器中的内容了。</p>
                  <p>这也解释了为什么有时我们得到的源代码和浏览器中看到的不一样。</p>
                  <p>因此，使用基本HTTP请求库得到的源代码可能跟浏览器中的页面源代码不太一样。对于这样的情况，我们可以分析其后台Ajax接口，也可使用Selenium、Splash这样的库来实现模拟JavaScript渲染。</p>
                  <p>后面，我们会详细介绍如何采集JavaScript渲染的网页。</p>
                  <p>本节介绍了爬虫的一些基本原理，这可以帮助我们在后面编写爬虫时更加得心应手。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 11:23:54" itemprop="dateCreated datePublished" datetime="2018-01-26T11:23:54+08:00">2018-01-26</time>
                </span>
                <span id="/5484.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2.3-爬虫的基本原理" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.3k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5476.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5476.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2.2-网页基础</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>用浏览器访问网站时，页面各不相同，你有没有想过它为何会呈现这个样子呢？本节中，我们就来了解一下网页的基本组成、结构和节点等内容。</p>
                  <h2 id="1-网页的组成"><a href="#1-网页的组成" class="headerlink" title="1. 网页的组成"></a>1. 网页的组成</h2>
                  <p>网页可以分为三大部分——HTML、CSS和JavaScript。如果把网页比作一个人的话，HTML相当于骨架，JavaScript相当于肌肉，CSS相当于皮肤，三者结合起来才能形成一个完善的网页。下面我们分别来介绍一下这三部分的功能。</p>
                  <h3 id="1-HTML"><a href="#1-HTML" class="headerlink" title="(1) HTML"></a>(1) HTML</h3>
                  <p>HTML是用来描述网页的一种语言，其全称叫作Hyper Text Markup Language，即超文本标记语言。网页包括文字、按钮、图片和视频等各种复杂的元素，其基础架构就是HTML。不同类型的文字通过不同类型的标签来表示，如图片用<code>img</code>标签表示，视频用<code>video</code>标签表示，段落用<code>p</code>标签表示，它们之间的布局又常通过布局标签<code>div</code>嵌套组合而成，各种标签通过不同的排列和嵌套才形成了网页的框架。</p>
                  <p>在Chrome浏览器中打开百度，右击并选择“检查”项（或按F12键），打开开发者模式，这时在Elements选项卡中即可看到网页的源代码，如图2-9所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-10.png" alt="">图2-9 源代码</p>
                  <p>这就是HTML，整个网页就是由各种标签嵌套组合而成的。这些标签定义的节点元素相互嵌套和组合形成了复杂的层次关系，就形成了网页的架构。</p>
                  <h3 id="2-CSS"><a href="#2-CSS" class="headerlink" title="(2) CSS"></a>(2) CSS</h3>
                  <p>HTML定义了网页的结构，但是只有HTML页面的布局并不美观，可能只是简单的节点元素的排列，为了让网页看起来更好看一些，这里借助了CSS。</p>
                  <p>CSS，全称叫作Cascading Style Sheets，即层叠样式表。“层叠”是指当在HTML中引用了数个样式文件，并且样式发生冲突时，浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等格式。</p>
                  <p>CSS是目前唯一的网页页面排版样式标准，有了它的帮助，页面才会变得更为美观。</p>
                  <p>图2-9的右侧即为CSS，例如：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-id">#head_wrapper</span><span class="selector-class">.s-ps-islite</span> <span class="selector-class">.s-p-top</span> &#123;</span><br><span class="line">    <span class="attribute">position</span>: absolute;</span><br><span class="line">    <span class="attribute">bottom</span>: <span class="number">40px</span>;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">181px</span>;</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>就是一个CSS样式。大括号前面是一个CSS选择器，此选择器的意思是首先选中<code>id</code>为<code>head_wrapper</code>且<code>class</code>为<code>s-ps-islite</code>的节点，然后再选中其内部的<code>class</code>为<code>s-p-top</code>的节点。大括号内部写的就是一条条样式规则，例如<code>position</code>指定了这个元素的布局方式为绝对布局，<code>bottom</code>指定元素的下边距为40像素，<code>width</code>指定了宽度为100%占满父元素，<code>height</code>则指定了元素的高度。也就是说，我们将位置、宽度、高度等样式配置统一写成这样的形式，然后用大括号括起来，接着在开头再加上CSS选择器，这就代表这个样式对CSS选择器选中的元素生效，元素就会根据此样式来展示了。</p>
                  <p>在网页中，一般会统一定义整个网页的样式规则，并写入CSS文件中（其后缀为css）。在HTML中，只需要用<code>link</code>标签即可引入写好的CSS文件，这样整个页面就会变得美观、优雅。</p>
                  <h3 id="3-JavaScript"><a href="#3-JavaScript" class="headerlink" title="(3) JavaScript"></a>(3) JavaScript</h3>
                  <p>JavaScript，简称JS，是一种脚本语言。HTML和CSS配合使用，提供给用户的只是一种静态信息，缺乏交互性。我们在网页里可能会看到一些交互和动画效果，如下载进度条、提示框、轮播图等，这通常就是JavaScript的功劳。它的出现使得用户与信息之间不只是一种浏览与显示的关系，而是实现了一种实时、动态、交互的页面功能。</p>
                  <p>JavaScript通常也是以单独的文件形式加载的，后缀为js，在HTML中通过<code>script</code>标签即可引入，例如：</p>
                  <figure class="highlight xml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"jquery-2.1.0.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>综上所述，HTML定义了网页的内容和结构，CSS描述了网页的布局，JavaScript定义了网页的行为。</p>
                  <h2 id="2-网页的结构"><a href="#2-网页的结构" class="headerlink" title="2. 网页的结构"></a>2. 网页的结构</h2>
                  <p>我们首先用例子来感受一下HTML的基本结构。新建一个文本文件，名称可以自取，后缀为html，内容如下：</p>
                  <figure class="highlight xml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">title</span>&gt;</span>This is a Demo<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"wrapper"</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">h2</span> <span class="attr">class</span>=<span class="string">"title"</span>&gt;</span>Hello World<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">p</span> <span class="attr">class</span>=<span class="string">"text"</span>&gt;</span>Hello, this is a paragraph.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这就是一个最简单的HTML实例。开头用<code>DOCTYPE</code>定义了文档类型，其次最外层是<code>html</code>标签，最后还有对应的结束标签来表示闭合，其内部是<code>head</code>标签和<code>body</code>标签，分别代表网页头和网页体，它们也需要结束标签。<code>head</code>标签内定义了一些页面的配置和引用，如：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;meta <span class="attribute">charset</span>=<span class="string">"UTF-8"</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>它指定了网页的编码为UTF-8。</p>
                  <p><code>title</code>标签则定义了网页的标题，会显示在网页的选项卡中，不会显示在正文中。<code>body</code>标签内则是在网页正文中显示的内容。<code>div</code>标签定义了网页中的区块，它的<code>id</code>是<code>container</code>，这是一个非常常用的属性，且<code>id</code>的内容在网页中是唯一的，我们可以通过它来获取这个区块。然后在此区块内又有一个<code>div</code>标签，它的<code>class</code>为<code>wrapper</code>，这也是一个非常常用的属性，经常与CSS配合使用来设定样式。然后此区块内部又有一个<code>h2</code>标签，这代表一个二级标题。另外，还有一个<code>p</code>标签，这代表一个段落。在这两者中直接写入相应的内容即可在网页中呈现出来，它们也有各自的<code>class</code>属性。</p>
                  <p>将代码保存后，在浏览器中打开该文件，可以看到如图2-10所示的内容。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-11.png" alt="">图2-10 运行结果</p>
                  <p>可以看到，在选项卡上显示了This is a Demo字样，这是我们在<code>head</code>中的<code>title</code>里定义的文字。而网页正文是<code>body</code>标签内部定义的各个元素生成的，可以看到这里显示了二级标题和段落。</p>
                  <p>这个实例便是网页的一般结构。一个网页的标准形式是<code>html</code>标签内嵌套<code>head</code>和<code>body</code>标签，<code>head</code>内定义网页的配置和引用，<code>body</code>内定义网页的正文。</p>
                  <h2 id="3-节点树及节点间的关系"><a href="#3-节点树及节点间的关系" class="headerlink" title="3. 节点树及节点间的关系"></a>3. 节点树及节点间的关系</h2>
                  <p>在HTML中，所有标签定义的内容都是节点，它们构成了一个HTML DOM树。</p>
                  <p>我们先看下什么是DOM，DOM是W3C（万维网联盟）的标准，其英文全称Document Object Model，即文档对象模型。它定义了访问HTML和XML文档的标准：</p>
                  <blockquote>
                    <p>W3C文档对象模型（DOM）是中立于平台和语言的接口，它允许程序和脚本动态地访问和更新文档的内容、结构和样式。</p>
                  </blockquote>
                  <p>W3C DOM标准被分为3个不同的部分。</p>
                  <ul>
                    <li><strong>核心DOM</strong>： 针对任何结构化文档的标准模型。</li>
                    <li><strong>XML DOM</strong>：针对XML文档的标准模型。</li>
                    <li><strong>HTML DOM</strong>：针对HTML文档的标准模型。</li>
                  </ul>
                  <p>根据W3C的HTML DOM标准，HTML文档中的所有内容都是节点。</p>
                  <ul>
                    <li>整个文档是一个文档节点；</li>
                    <li>每个HTML元素是元素节点；</li>
                    <li>HTML元素内的文本是文本节点；</li>
                    <li>每个HTML属性是属性节点；</li>
                    <li>注释是注释节点。</li>
                  </ul>
                  <p>HTML DOM将HTML文档视作树结构，这种结构被称为节点树，如图2-11所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-12.jpg" alt="">图2-11 节点树</p>
                  <p>通过HTML DOM，树中的所有节点均可通过JavaScript访问，所有HTML节点元素均可被修改，也可以被创建或删除。</p>
                  <p>节点树中的节点彼此拥有层级关系。我们常用父（parent）、子（child）和兄弟（sibling）等术语描述这些关系。父节点拥有子节点，同级的子节点被称为兄弟节点。</p>
                  <p>在节点树中，顶端节点称为根（root）。除了根节点之外，每个节点都有父节点，同时可拥有任意数量的子节点或兄弟节点。图2-12展示了节点树以及节点之间的关系。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-13.jpg" alt="">图2-12 节点树及节点间的关系</p>
                  <p>本段参考W3SCHOOL，链接：<a href="http://www.w3school.com.cn/htmldom/dom_nodes.asp" target="_blank" rel="noopener">http://www.w3school.com.cn/htmldom/dom_nodes.asp</a>。</p>
                  <h2 id="4-选择器"><a href="#4-选择器" class="headerlink" title="4. 选择器"></a>4. 选择器</h2>
                  <p>我们知道网页由一个个节点组成，CSS选择器会根据不同的节点设置不同的样式规则，那么怎样来定位节点呢？</p>
                  <p>在CSS中，我们使用CSS选择器来定位节点。例如，上例中<code>div</code>节点的<code>id</code>为<code>container</code>，那么就可以表示为<code>#container</code>，其中<code>#</code>开头代表选择<code>id</code>，其后紧跟<code>id</code>的名称。另外，如果我们想选择<code>class</code>为<code>wrapper</code>的节点，便可以使用<code>.wrapper</code>，这里以点（.）开头代表选择<code>class</code>，其后紧跟<code>class</code>的名称。另外，还有一种选择方式，那就是根据标签名筛选，例如想选择二级标题，直接用<code>h2</code>即可。这是最常用的3种表示，分别是根据<code>id</code>、<code>class</code>、标签名筛选，请牢记它们的写法。</p>
                  <p>另外，CSS选择器还支持嵌套选择，各个选择器之间加上空格分隔开便可以代表嵌套关系，如<code>#container .wrapper p</code>则代表先选择<code>id</code>为<code>container</code>的节点，然后选中其内部的<code>class</code>为<code>wrapper</code>的节点，然后再进一步选中其内部的<code>p</code>节点。另外，如果不加空格，则代表并列关系，如<code>div#container .wrapper p.text</code>代表先选择<code>id</code>为<code>container</code>的<code>div</code>节点，然后选中其内部的<code>class</code>为<code>wrapper</code>的节点，再进一步选中其内部的<code>class</code>为<code>text</code>的<code>p</code>节点。这就是CSS选择器，其筛选功能还是非常强大的。</p>
                  <p>另外，CSS选择器还有一些其他语法规则，具体如表2-4所示。</p>
                  <p>表2-4 CSS选择器的其他语法规则</p>
                  <p>选择器</p>
                  <p>例子</p>
                  <p>例子描述</p>
                  <p><code>.class</code></p>
                  <p><code>.intro</code></p>
                  <p>选择<code>class=&quot;intro&quot;</code>的所有节点</p>
                  <p><code>#id</code></p>
                  <p><code>#firstname</code></p>
                  <p>选择<code>id=&quot;firstname&quot;</code>的所有节点</p>
                  <p><code>*</code></p>
                  <p><code>*</code></p>
                  <p>选择所有节点</p>
                  <p><code>element</code></p>
                  <p><code>p</code></p>
                  <p>选择所有<code>p</code>节点</p>
                  <p><code>element,element</code></p>
                  <p><code>div,p</code></p>
                  <p>选择所有<code>div</code>节点和所有<code>p</code>节点</p>
                  <p><code>element element</code></p>
                  <p><code>div p</code></p>
                  <p>选择<code>div</code>节点内部的所有<code>p</code>节点</p>
                  <p><code>element&gt;element</code></p>
                  <p><code>div&gt;p</code></p>
                  <p>选择父节点为<code>div</code>节点的所有<code>p</code>节点</p>
                  <p><code>element+element</code></p>
                  <p><code>div+p</code></p>
                  <p>选择紧接在<code>div</code>节点之后的所有<code>p</code>节点</p>
                  <p><code>[attribute]</code></p>
                  <p><code>[target]</code></p>
                  <p>选择带有<code>target</code>属性的所有节点</p>
                  <p><code>[attribute=value]</code></p>
                  <p><code>[target=blank]</code></p>
                  <p>选择<code>target=&quot;blank&quot;</code>的所有节点</p>
                  <p><code>[attribute~=value]</code></p>
                  <p><code>[title~=flower]</code></p>
                  <p>选择<code>title</code>属性包含单词<code>flower</code>的所有节点</p>
                  <p><code>:link</code></p>
                  <p><code>a:link</code></p>
                  <p>选择所有未被访问的链接</p>
                  <p><code>:visited</code></p>
                  <p><code>a:visited</code></p>
                  <p>选择所有已被访问的链接</p>
                  <p><code>:active</code></p>
                  <p><code>a:active</code></p>
                  <p>选择活动链接</p>
                  <p><code>:hover</code></p>
                  <p><code>a:hover</code></p>
                  <p>选择鼠标指针位于其上的链接</p>
                  <p><code>:focus</code></p>
                  <p><code>input:focus</code></p>
                  <p>选择获得焦点的<code>input</code>节点</p>
                  <p><code>:first-letter</code></p>
                  <p><code>p:first-letter</code></p>
                  <p>选择每个<code>p</code>节点的首字母</p>
                  <p><code>:first-line</code></p>
                  <p><code>p:first-line</code></p>
                  <p>选择每个<code>p</code>节点的首行</p>
                  <p><code>:first-child</code></p>
                  <p><code>p:first-child</code></p>
                  <p>选择属于父节点的第一个子节点的所有<code>p</code>节点</p>
                  <p><code>:before</code></p>
                  <p><code>p:before</code></p>
                  <p>在每个<code>p</code>节点的内容之前插入内容</p>
                  <p><code>:after</code></p>
                  <p><code>p:after</code></p>
                  <p>在每个<code>p</code>节点的内容之后插入内容</p>
                  <p><code>:lang(language)</code></p>
                  <p><code>p:lang</code></p>
                  <p>选择带有以<code>it</code>开头的<code>lang</code>属性值的所有<code>p</code>节点</p>
                  <p><code>element1~element2</code></p>
                  <p><code>p~ul</code></p>
                  <p>选择前面有<code>p</code>节点的所有<code>ul</code>节点</p>
                  <p><code>[attribute^=value]</code></p>
                  <p><code>a[src^=&quot;https&quot;]</code></p>
                  <p>选择其<code>src</code>属性值以<code>https</code>开头的所有<code>a</code>节点</p>
                  <p><code>[attribute$=value]</code></p>
                  <p><code>a[src$=&quot;.pdf&quot;]</code></p>
                  <p>选择其<code>src</code>属性以<code>.pdf</code>结尾的所有<code>a</code>节点</p>
                  <p><code>[attribute*=value]</code></p>
                  <p><code>a[src*=&quot;abc&quot;]</code></p>
                  <p>选择其<code>src</code>属性中包含<code>abc</code>子串的所有<code>a</code>节点</p>
                  <p><code>:first-of-type</code></p>
                  <p><code>p:first-of-type</code></p>
                  <p>选择属于其父节点的首个<code>p</code>节点的所有<code>p</code>节点</p>
                  <p><code>:last-of-type</code></p>
                  <p><code>p:last-of-type</code></p>
                  <p>选择属于其父节点的最后<code>p</code>节点的所有<code>p</code>节点</p>
                  <p><code>:only-of-type</code></p>
                  <p><code>p:only-of-type</code></p>
                  <p>选择属于其父节点唯一的<code>p</code>节点的所有<code>p</code>节点</p>
                  <p><code>:only-child</code></p>
                  <p><code>p:only-child</code></p>
                  <p>选择属于其父节点的唯一子节点的所有<code>p</code>节点</p>
                  <p><code>:nth-child(n)</code></p>
                  <p><code>p:nth-child</code></p>
                  <p>选择属于其父节点的第二个子节点的所有<code>p</code>节点</p>
                  <p><code>:nth-last-child(n)</code></p>
                  <p><code>p:nth-last-child</code></p>
                  <p>同上，从最后一个子节点开始计数</p>
                  <p><code>:nth-of-type(n)</code></p>
                  <p><code>p:nth-of-type</code></p>
                  <p>选择属于其父节点第二个<code>p</code>节点的所有<code>p</code>节点</p>
                  <p><code>:nth-last-of-type(n)</code></p>
                  <p><code>p:nth-last-of-type</code></p>
                  <p>同上，但是从最后一个子节点开始计数</p>
                  <p><code>:last-child</code></p>
                  <p><code>p:last-child</code></p>
                  <p>选择属于其父节点最后一个子节点的所有<code>p</code>节点</p>
                  <p><code>:root</code></p>
                  <p><code>:root</code></p>
                  <p>选择文档的根节点</p>
                  <p><code>:empty</code></p>
                  <p><code>p:empty</code></p>
                  <p>选择没有子节点的所有<code>p</code>节点（包括文本节点）</p>
                  <p><code>:target</code></p>
                  <p><code>#news:target</code></p>
                  <p>选择当前活动的<code>#news</code>节点</p>
                  <p><code>:enabled</code></p>
                  <p><code>input:enabled</code></p>
                  <p>选择每个启用的<code>input</code>节点</p>
                  <p><code>:disabled</code></p>
                  <p><code>input:disabled</code></p>
                  <p>选择每个禁用的<code>input</code>节点</p>
                  <p><code>:checked</code></p>
                  <p><code>input:checked</code></p>
                  <p>选择每个被选中的<code>input</code>节点</p>
                  <p><code>:not(selector)</code></p>
                  <p><code>:not</code></p>
                  <p>选择非<code>p</code>节点的所有节点</p>
                  <p><code>::selection</code></p>
                  <p><code>::selection</code></p>
                  <p>选择被用户选取的节点部分</p>
                  <p>另外，还有一种比较常用的选择器是XPath，这种选择方式后面会详细介绍。</p>
                  <p>本节介绍了网页的基本结构和节点间的关系，了解了这些内容，我们才有更加清晰的思路去解析和提取网页内容。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 11:13:58" itemprop="dateCreated datePublished" datetime="2018-01-26T11:13:58+08:00">2018-01-26</time>
                </span>
                <span id="/5476.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2.2-网页基础" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>5.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5465.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5465.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2.1-HTTP基本原理</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在本节中，我们会详细了解HTTP的基本原理，了解在浏览器中敲入URL到获取网页内容之间发生了什么。了解了这些内容，有助于我们进一步了解爬虫的基本原理。</p>
                  <h2 id="1-URI和URL"><a href="#1-URI和URL" class="headerlink" title="1. URI和URL"></a>1. URI和URL</h2>
                  <p>这里我们先了解一下URI和URL，URI的全称为Uniform Resource Identifier，即统一资源标志符，URL的全称为Universal Resource Locator，即统一资源定位符。</p>
                  <p>举例来说，<a href="https://github.com/favicon.ico" target="_blank" rel="noopener">https://github.com/favicon.ico</a>是GitHub的网站图标链接，它是一个URL，也是一个URI。即有这样的一个图标资源，我们用URL/URI来唯一指定了它的访问方式，这其中包括了访问协议https、访问路径（/即根目录）和资源名称favicon.ico。通过这样一个链接，我们便可以从互联网上找到这个资源，这就是URL/URI。</p>
                  <p>URL是URI的子集，也就是说每个URL都是URI，但不是每个URI都是URL。那么，怎样的URI不是URL呢？URI还包括一个子类叫作URN，它的全称为Universal Resource Name，即统一资源名称。URN只命名资源而不指定如何定位资源，比如urn:isbn:0451450523指定了一本书的ISBN，可以唯一标识这本书，但是没有指定到哪里定位这本书，这就是URN。URL、URN和URI的关系可以用图2-1表示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-1.jpg" alt="">图2-1 URL、URN和URI关系图</p>
                  <p>但是在目前的互联网中，URN用得非常少，所以几乎所有的URI都是URL，一般的网页链接我们既可以称为URL，也可以称为URI，我个人习惯称为URL。</p>
                  <h2 id="2-超文本"><a href="#2-超文本" class="headerlink" title="2.  超文本"></a>2. 超文本</h2>
                  <p>接下来，我们再了解一个概念——超文本，其英文名称叫作hypertext，我们在浏览器里看到的网页就是超文本解析而成的，其网页源代码是一系列HTML代码，里面包含了一系列标签，比如<code>img</code>显示图片，<code>p</code>指定显示段落等。浏览器解析这些标签后，便形成了我们平常看到的网页，而网页的源代码HTML就可以称作超文本。</p>
                  <p>例如，我们在Chrome浏览器里面打开任意一个页面，如淘宝首页，右击任一地方并选择“检查”项（或者直接按快捷键F12），即可打开浏览器的开发者工具，这时在Elements选项卡即可看到当前网页的源代码，这些源代码都是超文本，如图2-2所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-2.png" alt="">图2-2 源代码</p>
                  <h2 id="3-HTTP和HTTPS"><a href="#3-HTTP和HTTPS" class="headerlink" title="3. HTTP和HTTPS"></a>3. HTTP和HTTPS</h2>
                  <p>在淘宝的首页<a href="https://www.taobao.com/" target="_blank" rel="noopener">https://www.taobao.com/</a>中，URL的开头会有http或https，这就是访问资源需要的协议类型。有时，我们还会看到ftp、sftp、smb开头的URL，它们都是协议类型。在爬虫中，我们抓取的页面通常就是http或https协议的，这里首先了解一下这两个协议的含义。</p>
                  <p>HTTP的全称是Hyper Text Transfer Protocol，中文名叫作超文本传输协议。HTTP协议是用于从网络传输超文本数据到本地浏览器的传送协议，它能保证高效而准确地传送超文本文档。HTTP由万维网协会（World Wide Web Consortium）和Internet工作小组IETF（Internet Engineering Task Force）共同合作制定的规范，目前广泛使用的是HTTP 1.1版本。</p>
                  <p>HTTPS的全称是Hyper Text Transfer Protocol over Secure Socket Layer，是以安全为目标的HTTP通道，简单讲是HTTP的安全版，即HTTP下加入SSL层，简称为HTTPS。</p>
                  <p>HTTPS的安全基础是SSL，因此通过它传输的内容都是经过SSL加密的，它的主要作用可以分为两种。</p>
                  <ul>
                    <li>建立一个信息安全通道来保证数据传输的安全。</li>
                    <li>确认网站的真实性，凡是使用了HTTPS的网站，都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息，也可以通过CA机构颁发的安全签章来查询。</li>
                  </ul>
                  <p>现在越来越多的网站和App都已经向HTTPS方向发展，例如：</p>
                  <ul>
                    <li>苹果公司强制所有iOS App在2017年1月1日前全部改为使用HTTPS加密，否则App就无法在应用商店上架；</li>
                    <li>谷歌从2017年1月推出的Chrome 56开始，对未进行HTTPS加密的网址链接亮出风险提示，即在地址栏的显著位置提醒用户“此网页不安全”；</li>
                    <li>腾讯微信小程序的官方需求文档要求后台使用HTTPS请求进行网络通信，不满足条件的域名和协议无法请求。</li>
                  </ul>
                  <p>而某些网站虽然使用了HTTPS协议，但还是会被浏览器提示不安全，例如我们在Chrome浏览器里面打开12306，链接为：<a href="https://www.12306.cn/" target="_blank" rel="noopener">https://www.12306.cn/</a>，这时浏览器就会提示“您的连接不是私密连接”这样的话，如图2-3所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-3.png" alt="">图2-3 12306页面</p>
                  <p>这是因为12306的CA证书是中国铁道部自行签发的，而这个证书是不被CA机构信任的，所以这里证书验证就不会通过而提示这样的话，但是实际上它的数据传输依然是经过SSL加密的。如果要爬取这样的站点，就需要设置忽略证书的选项，否则会提示SSL链接错误。</p>
                  <h2 id="4-HTTP请求过程"><a href="#4-HTTP请求过程" class="headerlink" title="4. HTTP请求过程"></a>4. HTTP请求过程</h2>
                  <p>我们在浏览器中输入一个URL，回车之后便会在浏览器中观察到页面内容。实际上，这个过程是浏览器向网站所在的服务器发送了一个请求，网站服务器接收到这个请求后进行处理和解析，然后返回对应的响应，接着传回给浏览器。响应里包含了页面的源代码等内容，浏览器再对其进行解析，便将网页呈现了出来，模型如图2-4所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-4.jpg" alt="">图2-4 模型图</p>
                  <p>此处客户端即代表我们自己的PC或手机浏览器，服务器即要访问的网站所在的服务器。</p>
                  <p>为了更直观地地说明这个过程，这里用Chrome浏览器的开发者模式下的Network监听组件来做下演示，它可以显示访问当前请求网页时发生的所有网络请求和响应。</p>
                  <p>打开Chrome浏览器，右击并选择“检查”项，即可打开浏览器的开发者工具。这里访问百度<a href="http://www.baidu.com/" target="_blank" rel="noopener">http://www.baidu.com/</a>，输入该URL后回车，观察这个过程中发生了怎样的网络请求。可以看到，在Network页面下方出现了一个个的条目，其中一个条目就代表一次发送请求和接收响应的过程，如图2-5所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-5.png" alt="">图2-5 Network面板</p>
                  <p>我们先观察第一个网络请求，即www.baidu.com。</p>
                  <p>其中各列的含义如下。</p>
                  <ul>
                    <li><strong>第一列Name</strong>：请求的名称，一般会将URL的最后一部分内容当作名称。</li>
                    <li><strong>第二列Status</strong>：响应的状态码，这里显示为200，代表响应是正常的。通过状态码，我们可以判断发送了请求之后是否得到了正常的响应。</li>
                    <li><strong>第三列Type</strong>：请求的文档类型。这里为document，代表我们这次请求的是一个HTML文档，内容就是一些HTML代码。</li>
                    <li><strong>第四列Initiator</strong>：请求源。用来标记请求是由哪个对象或进程发起的。</li>
                    <li><strong>第五列Size</strong>：从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源，则该列会显示from cache。</li>
                    <li><strong>第六列Time</strong>：发起请求到获取响应所用的总时间。</li>
                    <li><strong>第七列Waterfall</strong>：网络请求的可视化瀑布流。</li>
                  </ul>
                  <p>点击这个条目，即可看到更详细的信息，如图2-6所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-6.jpg" alt="">图2-6 详细信息</p>
                  <p>首先是General部分，Request URL为请求的URL，Request Method为请求的方法，Status Code为响应状态码，Remote Address为远程服务器的地址和端口，Referrer Policy为Referrer判别策略。</p>
                  <p>再继续往下看，可以看到，有Response Headers和Request Headers，这分别代表响应头和请求头。请求头里带有许多请求信息，例如浏览器标识、Cookies、Host等信息，这是请求的一部分，服务器会根据请求头内的信息判断请求是否合法，进而作出对应的响应。图中看到的Response Headers就是响应的一部分，例如其中包含了服务器的类型、文档类型、日期等信息，浏览器接受到响应后，会解析响应内容，进而呈现网页内容。</p>
                  <p>下面我们分别来介绍一下请求和响应都包含哪些内容。</p>
                  <h2 id="5-请求"><a href="#5-请求" class="headerlink" title="5. 请求"></a>5. 请求</h2>
                  <p>请求，由客户端向服务端发出，可以分为4部分内容：请求方法（Request Method）、请求的网址（Request URL）、请求头（Request Headers）、请求体（Request Body）。</p>
                  <h3 id="1-请求方法"><a href="#1-请求方法" class="headerlink" title="(1) 请求方法"></a>(1) 请求方法</h3>
                  <p>常见的请求方法有两种：GET和POST。</p>
                  <p>在浏览器中直接输入URL并回车，这便发起了一个GET请求，请求的参数会直接包含到URL里。例如，在百度中搜索Python，这就是一个GET请求，链接为<a href="https://www.baidu.com/s?wd=Python" target="_blank" rel="noopener">https://www.baidu.com/s?wd=Python</a>，其中URL中包含了请求的参数信息，这里参数<code>wd</code>表示要搜寻的关键字。POST请求大多在表单提交时发起。比如，对于一个登录表单，输入用户名和密码后，点击“登录”按钮，这通常会发起一个POST请求，其数据通常以表单的形式传输，而不会体现在URL中。</p>
                  <p>GET和POST请求方法有如下区别。</p>
                  <ul>
                    <li>GET请求中的参数包含在URL里面，数据可以在URL中看到，而POST请求的URL不会包含这些数据，数据都是通过表单形式传输的，会包含在请求体中。</li>
                    <li>GET请求提交的数据最多只有1024字节，而POST方式没有限制。</li>
                  </ul>
                  <p>一般来说，登录时，需要提交用户名和密码，其中包含了敏感信息，使用GET方式请求的话，密码就会暴露在URL里面，造成密码泄露，所以这里最好以POST方式发送。上传文件时，由于文件内容比较大，也会选用POST方式。</p>
                  <p>我们平常遇到的绝大部分请求都是GET或POST请求，另外还有一些请求方法，如GET、HEAD、POST、PUT、DELETE、OPTIONS、CONNECT、TRACE等，我们简单将其总结为表2-1。</p>
                  <p>表2-1 其他请求方法</p>
                  <p>方法</p>
                  <p>描述</p>
                  <p>GET</p>
                  <p>请求页面，并返回页面内容</p>
                  <p>HEAD</p>
                  <p>类似于GET请求，只不过返回的响应中没有具体的内容，用于获取报头</p>
                  <p>POST</p>
                  <p>大多用于提交表单或上传文件，数据包含在请求体中</p>
                  <p>PUT</p>
                  <p>从客户端向服务器传送的数据取代指定文档中的内容</p>
                  <p>DELETE</p>
                  <p>请求服务器删除指定的页面</p>
                  <p>CONNECT</p>
                  <p>把服务器当作跳板，让服务器代替客户端访问其他网页</p>
                  <p>OPTIONS</p>
                  <p>允许客户端查看服务器的性能</p>
                  <p>TRACE</p>
                  <p>回显服务器收到的请求，主要用于测试或诊断</p>
                  <p>本表参考：<a href="http://www.runoob.com/http/http-methods.html" target="_blank" rel="noopener">http://www.runoob.com/http/http-methods.html</a>。</p>
                  <h3 id="2-请求的网址"><a href="#2-请求的网址" class="headerlink" title="(2) 请求的网址"></a>(2) 请求的网址</h3>
                  <p>请求的网址，即统一资源定位符URL，它可以唯一确定我们想请求的资源。</p>
                  <h3 id="3-请求头"><a href="#3-请求头" class="headerlink" title="(3) 请求头"></a>(3) 请求头</h3>
                  <p>请求头，用来说明服务器要使用的附加信息，比较重要的信息有Cookie、Referer、User-Agent等。下面简要说明一些常用的头信息。</p>
                  <ul>
                    <li><strong>Accept</strong>：请求报头域，用于指定客户端可接受哪些类型的信息。</li>
                    <li><strong>Accept-Language</strong>：指定客户端可接受的语言类型。</li>
                    <li><strong>Accept-Encoding</strong>：指定客户端可接受的内容编码。</li>
                    <li><strong>Host</strong>：用于指定请求资源的主机IP和端口号，其内容为请求URL的原始服务器或网关的位置。从HTTP 1.1版本开始，请求必须包含此内容。</li>
                    <li><strong>Cookie</strong>：也常用复数形式 Cookies，这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。例如，我们输入用户名和密码成功登录某个网站后，服务器会用会话保存登录状态信息，后面我们每次刷新或请求该站点的其他页面时，会发现都是登录状态，这就是Cookies的功劳。Cookies里有信息标识了我们所对应的服务器的会话，每次浏览器在请求该站点的页面时，都会在请求头中加上Cookies并将其发送给服务器，服务器通过Cookies识别出是我们自己，并且查出当前状态是登录状态，所以返回结果就是登录之后才能看到的网页内容。</li>
                    <li><strong>Referer</strong>：此内容用来标识这个请求是从哪个页面发过来的，服务器可以拿到这一信息并做相应的处理，如作来源统计、防盗链处理等。</li>
                    <li><strong>User-Agent</strong>：简称UA，它是一个特殊的字符串头，可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息，可以伪装为浏览器；如果不加，很可能会被识别出为爬虫。</li>
                    <li><strong>Content-Type</strong>：也叫互联网媒体类型（Internet Media Type）或者MIME类型，在HTTP协议消息头中，它用来表示具体请求中的媒体类型信息。例如，text/html代表HTML格式，image/gif代表GIF图片，application/json代表JSON类型，更多对应关系可以查看此对照表：<a href="http://tool.oschina.net/commons" target="_blank" rel="noopener">http://tool.oschina.net/commons</a>。</li>
                  </ul>
                  <p>因此，请求头是请求的重要组成部分，在写爬虫时，大部分情况下都需要设定请求头。</p>
                  <h3 id="4-请求体"><a href="#4-请求体" class="headerlink" title="(4) 请求体"></a>(4) 请求体</h3>
                  <p>请求体一般承载的内容是POST请求中的表单数据，而对于GET请求，请求体则为空。</p>
                  <p>例如，这里我登录GitHub时捕获到的请求和响应如图2-7所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-7.jpg" alt="">图2-7 详细信息</p>
                  <p>登录之前，我们填写了用户名和密码信息，提交时这些内容就会以表单数据的形式提交给服务器，此时需要注意Request Headers中指定Content-Type为application/x-www-form-urlencoded。只有设置Content-Type为application/x-www-form-urlencoded，才会以表单数据的形式提交。另外，我们也可以将Content-Type设置为application/json来提交JSON数据，或者设置为multipart/form-data来上传文件。</p>
                  <p>表2-2列出了Content-Type和POST提交数据方式的关系。</p>
                  <p>表2-2 Content-Type和POST提交数据方式的关系</p>
                  <p>Content-Type</p>
                  <p>提交数据的方式</p>
                  <p>application/x-www-form-urlencoded</p>
                  <p>表单数据</p>
                  <p>multipart/form-data</p>
                  <p>表单文件上传</p>
                  <p>application/json</p>
                  <p>序列化JSON数据</p>
                  <p>text/xml</p>
                  <p>XML数据</p>
                  <p>在爬虫中，如果要构造POST请求，需要使用正确的Content-Type，并了解各种请求库的各个参数设置时使用的是哪种Content-Type，不然可能会导致POST提交后无法正常响应。</p>
                  <h2 id="6-响应"><a href="#6-响应" class="headerlink" title="6. 响应"></a>6. 响应</h2>
                  <p>响应，由服务端返回给客户端，可以分为三部分：响应状态码（Response Status Code）、响应头（Response Headers）和响应体（Response Body）。</p>
                  <h3 id="1-响应状态码"><a href="#1-响应状态码" class="headerlink" title="(1) 响应状态码"></a>(1) 响应状态码</h3>
                  <p>响应状态码表示服务器的响应状态，如200代表服务器正常响应，404代表页面未找到，500代表服务器内部发生错误。在爬虫中，我们可以根据状态码来判断服务器响应状态，如状态码为200，则证明成功返回数据，再进行进一步的处理，否则直接忽略。表2-3列出了常见的错误代码及错误原因。</p>
                  <p>表2-3 常见的错误代码及错误原因</p>
                  <p>状态码</p>
                  <p>说明</p>
                  <p>详情</p>
                  <p>100</p>
                  <p>继续</p>
                  <p>请求者应当继续提出请求。服务器已收到请求的一部分，正在等待其余部分</p>
                  <p>101</p>
                  <p>切换协议</p>
                  <p>请求者已要求服务器切换协议，服务器已确认并准备切换</p>
                  <p>200</p>
                  <p>成功</p>
                  <p>服务器已成功处理了请求</p>
                  <p>201</p>
                  <p>已创建</p>
                  <p>请求成功并且服务器创建了新的资源</p>
                  <p>202</p>
                  <p>已接受</p>
                  <p>服务器已接受请求，但尚未处理</p>
                  <p>203</p>
                  <p>非授权信息</p>
                  <p>服务器已成功处理了请求，但返回的信息可能来自另一个源</p>
                  <p>204</p>
                  <p>无内容</p>
                  <p>服务器成功处理了请求，但没有返回任何内容</p>
                  <p>205</p>
                  <p>重置内容</p>
                  <p>服务器成功处理了请求，内容被重置</p>
                  <p>206</p>
                  <p>部分内容</p>
                  <p>服务器成功处理了部分请求</p>
                  <p>300</p>
                  <p>多种选择</p>
                  <p>针对请求，服务器可执行多种操作</p>
                  <p>301</p>
                  <p>永久移动</p>
                  <p>请求的网页已永久移动到新位置，即永久重定向</p>
                  <p>302</p>
                  <p>临时移动</p>
                  <p>请求的网页暂时跳转到其他页面，即暂时重定向</p>
                  <p>303</p>
                  <p>查看其他位置</p>
                  <p>如果原来的请求是POST，重定向目标文档应该通过GET提取</p>
                  <p>304</p>
                  <p>未修改</p>
                  <p>此次请求返回的网页未修改，继续使用上次的资源</p>
                  <p>305</p>
                  <p>使用代理</p>
                  <p>请求者应该使用代理访问该网页</p>
                  <p>307</p>
                  <p>临时重定向</p>
                  <p>请求的资源临时从其他位置响应</p>
                  <p>400</p>
                  <p>错误请求</p>
                  <p>服务器无法解析该请求</p>
                  <p>401</p>
                  <p>未授权</p>
                  <p>请求没有进行身份验证或验证未通过</p>
                  <p>403</p>
                  <p>禁止访问</p>
                  <p>服务器拒绝此请求</p>
                  <p>404</p>
                  <p>未找到</p>
                  <p>服务器找不到请求的网页</p>
                  <p>405</p>
                  <p>方法禁用</p>
                  <p>服务器禁用了请求中指定的方法</p>
                  <p>406</p>
                  <p>不接受</p>
                  <p>无法使用请求的内容响应请求的网页</p>
                  <p>407</p>
                  <p>需要代理授权</p>
                  <p>请求者需要使用代理授权</p>
                  <p>408</p>
                  <p>请求超时</p>
                  <p>服务器请求超时</p>
                  <p>409</p>
                  <p>冲突</p>
                  <p>服务器在完成请求时发生冲突</p>
                  <p>410</p>
                  <p>已删除</p>
                  <p>请求的资源已永久删除</p>
                  <p>411</p>
                  <p>需要有效长度</p>
                  <p>服务器不接受不含有效内容长度标头字段的请求</p>
                  <p>412</p>
                  <p>未满足前提条件</p>
                  <p>服务器未满足请求者在请求中设置的其中一个前提条件</p>
                  <p>413</p>
                  <p>请求实体过大</p>
                  <p>请求实体过大，超出服务器的处理能力</p>
                  <p>414</p>
                  <p>请求URI过长</p>
                  <p>请求网址过长，服务器无法处理</p>
                  <p>415</p>
                  <p>不支持类型</p>
                  <p>请求格式不被请求页面支持</p>
                  <p>416</p>
                  <p>请求范围不符</p>
                  <p>页面无法提供请求的范围</p>
                  <p>417</p>
                  <p>未满足期望值</p>
                  <p>服务器未满足期望请求标头字段的要求</p>
                  <p>500</p>
                  <p>服务器内部错误</p>
                  <p>服务器遇到错误，无法完成请求</p>
                  <p>501</p>
                  <p>未实现</p>
                  <p>服务器不具备完成请求的功能</p>
                  <p>502</p>
                  <p>错误网关</p>
                  <p>服务器作为网关或代理，从上游服务器收到无效响应</p>
                  <p>503</p>
                  <p>服务不可用</p>
                  <p>服务器目前无法使用</p>
                  <p>504</p>
                  <p>网关超时</p>
                  <p>服务器作为网关或代理，但是没有及时从上游服务器收到请求</p>
                  <p>505</p>
                  <p>HTTP版本不支持</p>
                  <p>服务器不支持请求中所用的HTTP协议版本</p>
                  <h3 id="2-响应头"><a href="#2-响应头" class="headerlink" title="(2) 响应头"></a>(2) 响应头</h3>
                  <p>响应头包含了服务器对请求的应答信息，如Content-Type、Server、Set-Cookie等。下面简要说明一些常用的头信息。</p>
                  <ul>
                    <li><strong>Date</strong>：标识响应产生的时间。</li>
                    <li><strong>Last-Modified</strong>：指定资源的最后修改时间。</li>
                    <li><strong>Content-Encoding</strong>：指定响应内容的编码。</li>
                    <li><strong>Server</strong>：包含服务器的信息，比如名称、版本号等。</li>
                    <li><strong>Content-Type</strong>：文档类型，指定返回的数据类型是什么，如text/html代表返回HTML文档，application/x-javascript则代表返回JavaScript文件，image/jpeg则代表返回图片。</li>
                    <li><strong>Set-Cookie</strong>：设置Cookies。响应头中的Set-Cookie告诉浏览器需要将此内容放在Cookies中，下次请求携带Cookies请求。</li>
                    <li><strong>Expires</strong>：指定响应的过期时间，可以使代理服务器或浏览器将加载的内容更新到缓存中。如果再次访问时，就可以直接从缓存中加载，降低服务器负载，缩短加载时间。</li>
                  </ul>
                  <h3 id="3-响应体"><a href="#3-响应体" class="headerlink" title="(3) 响应体"></a>(3) 响应体</h3>
                  <p>最重要的当属响应体的内容了。响应的正文数据都在响应体中，比如请求网页时，它的响应体就是网页的HTML代码；请求一张图片时，它的响应体就是图片的二进制数据。我们做爬虫请求网页后，要解析的内容就是响应体，如图2-8所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/2-8.jpg" alt="">图2-8 响应体内容</p>
                  <p>在浏览器开发者工具中点击Preview，就可以看到网页的源代码，也就是响应体的内容，它是解析的目标。</p>
                  <p>在做爬虫时，我们主要通过响应体得到网页的源代码、JSON数据等，然后从中做相应内容的提取。</p>
                  <p>本节中，我们了解了HTTP的基本原理，大概了解了访问网页时背后的请求和响应过程。本节涉及的知识点需要好好掌握，后面分析网页请求时会经常用到。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 11:02:37" itemprop="dateCreated datePublished" datetime="2018-01-26T11:02:37+08:00">2018-01-26</time>
                </span>
                <span id="/5465.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2.1-HTTP基本原理" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>7.6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>7 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5462.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5462.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 2-爬虫基础</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在写爬虫之前，我们还需要了解一些基础知识，如HTTP原理、网页的基础知识、爬虫的基本原理、Cookies的基本原理等。本章中，我们就对这些基础知识做一个简单的总结。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 10:57:45" itemprop="dateCreated datePublished" datetime="2018-01-26T10:57:45+08:00">2018-01-26</time>
                </span>
                <span id="/5462.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 2-爬虫基础" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>82</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5024.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5024.html" class="post-title-link" itemprop="url">JavaScript加密逻辑分析与Python模拟执行实现数据爬取</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>本节来说明一下 JavaScript 加密逻辑分析并利用 Python 模拟执行 JavaScript 实现数据爬取的过程。在这里以中国空气质量在线监测分析平台为例来进行分析，主要分析其加密逻辑及破解方法，并利用 PyExecJS 来实现 JavaScript 模拟执行来实现该网站的数据爬取。</p>
                  <h2 id="疑难杂症"><a href="#疑难杂症" class="headerlink" title="疑难杂症"></a>疑难杂症</h2>
                  <p>中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站，包括温度、湿度、PM 2.5、AQI 等数据，链接为：<a href="https://www.aqistudy.cn/html/city_detail.html" target="_blank" rel="noopener">https://www.aqistudy.cn/html/city_detail.html</a>，预览图如下： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-223705-1100x456.png" alt="">通过这个网站我们可以获取到各大城市任何一天的天气数据，对数据分析还是非常有用的。 然而不幸的是，该网站的数据接口通信都被加密了。经过分析之后发现其页面数据是通过 Ajax 加载的，数据接口地址是：<a href="https://www.aqistudy.cn/apinew/aqistudyapi.php" target="_blank" rel="noopener">https://www.aqistudy.cn/apinew/aqistudyapi.php</a>，是一个 POST 形式访问的接口，这个接口的请求数据和返回数据都被加密了，即 POST 请求的 Data、返回的数据都被加密了，下图是数据接口的 Form Data 部分，可见传输数据是一个加密后的字符串： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-223517.png" alt=""> 下图是该接口返回的内容，同样是经过加密的字符串： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-223555.png" alt=""> 遇到这种接口加密的情况，一般来说我们会选择避开请求接口的方式进行数据爬取，如使用 Selenium 模拟浏览器来执行。但这个网站的数据是图表展示的，所以其数据会变得难以提取。 那怎么办呢？刚啊！</p>
                  <h2 id="一刚到底"><a href="#一刚到底" class="headerlink" title="一刚到底"></a>一刚到底</h2>
                  <p>之前的老法子都行不通了，那就只能上了！接下来我们就不得不去分析这个网站接口的加密逻辑，并通过一些技巧来破解这个接口了。 首先找到突破口，当我们点击了这个搜索按钮之后，后台便会发出 Ajax 请求，说明这个点击动作是被监听的，所以我们可以找一下这个点击事件对应的处理代码在哪里，这里可以借助于 Firefox 来实现，它可以分析页面某个元素的绑定事件以及定位到具体的代码在哪一行，如图所示： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-225414@2x-1100x704.png" alt=""> 这里我们发现这个搜索按钮绑定了三个事件，blur、click、focus，同时 Firefox 还帮助我们列出来了对应事件的处理函数在哪个代码的哪一行，这里可以看到 click 事件是在 city_detail.html 的第 139 行处理的，而且是调用了 getData() 函数。 接下来我们就可以顺藤摸瓜，找到 city_detail.html 文件的 getData() 函数，然后再找到这个函数的定义即可，很容易地，我们在 city_detail.html 的第 463 行就找到了这个函数的定义： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-230128@2x-1100x415.png" alt=""> 经过分析发现它又调用了 getAQIData() 和 getWeatherData() 两个方法，而这两个方法的声明就在下面，再进一步分析发现这两个方法都调用了 getServerData() 这个方法，并传递了 method、param 等参数，然后还有一个回调函数很明显是对返回数据进行处理的，这说明 Ajax 请求就是由这个 getServerData() 方法发起的，如图所示： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-230433@2x-1100x875.png" alt=""> 所以这里我们只需要再找到 getServerData() 方法的定义即可分析它的加密逻辑了。继续搜索，然而在原始 html 文件中没有搜索到该方法，那就继续去搜寻其他的 JavaScript 文件有没有这个定义，终于经过一番寻找，居然在 jquery-1.8.0.min.js 这个文件中找到了： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-230819@2x-1100x303.png" alt=""> 有的小伙伴可能会说，jquery.min.js 不是一个库文件吗，怎么会有这种方法声明？嗯，我只想说，最危险的地方就是最安全的地方。 好了，现在终于找到这个方法了，可为什么看不懂呢？这个方法名后面怎么直接跟了一些奇怪的字符串，而且不符合一般的 JavaScript 写法。其实这里是经过 JavaScript 混淆加密了，混淆加密之后，代码将变为不可读的形式，但是功能是完全一致的，这是一种常见的 JavaScript 加密手段。 那到这里了该怎么解呢？当然是接着刚啊！</p>
                  <h2 id="反混淆"><a href="#反混淆" class="headerlink" title="反混淆"></a>反混淆</h2>
                  <p>JavaScript 混淆之后，其实是有反混淆方法的，最简单的方法便是搜索在线反混淆网站，这里提供一个：<a href="http://www.bm8.com.cn/jsConfusion/" target="_blank" rel="noopener">http://www.bm8.com.cn/jsConfusion/</a>，我们将 jquery-1.8.0.min.js 中第二行 eval 开头的混淆后的 JavaScript 代码复制一下，然后粘贴到这个网站中进行反混淆，就可以看到正常的 JavaScript 代码了，搜索一下就可以找到 getServerData() 方法了，可以看到这个方法确实发出了一个 Ajax 请求，请求了刚才我们分析到的接口： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/01/WX20180125-231719.png" alt=""> 那么到这里我们又可以发现一个很关键的方法，那就是 getParam()，它接受了 method 和 object 参数，然后返回得到的 param 结果就作为 POST Data 参数请求接口了，所以 param 就是加密后的 POST Data，一些加密逻辑都在 getParam() 方法里面，其方法实现如下：</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">var getParam = (<span class="name">function</span> () &#123;</span><br><span class="line">        function ObjectSort(<span class="name">obj</span>) &#123;</span><br><span class="line">            var newObject = &#123;&#125;<span class="comment">;</span></span><br><span class="line">            Object.keys(<span class="name">obj</span>).sort().map(<span class="name">function</span> (<span class="name">key</span>) &#123;</span><br><span class="line">                newObject[key] = obj[key]</span><br><span class="line">            &#125;)<span class="comment">;</span></span><br><span class="line">            return newObject</span><br><span class="line">        &#125;</span><br><span class="line">        return function (<span class="name">method</span>, obj) &#123;</span><br><span class="line">            var appId = '<span class="number">1</span>a45f75b824b2dc628d5955356b5ef18'<span class="comment">;</span></span><br><span class="line">            var clienttype = 'WEB'<span class="comment">;</span></span><br><span class="line">            var timestamp = new Date().getTime()<span class="comment">;</span></span><br><span class="line">            var param = &#123;</span><br><span class="line">                appId: appId,</span><br><span class="line">                method: method,</span><br><span class="line">                timestamp: timestamp,</span><br><span class="line">                clienttype: clienttype,</span><br><span class="line">                object: obj,</span><br><span class="line">                secret: hex_md5(<span class="name">appId</span> + method + timestamp + clienttype + JSON.stringify(<span class="name">ObjectSort</span>(<span class="name">obj</span>)))</span><br><span class="line">            &#125;<span class="comment">;</span></span><br><span class="line">            param = BASE64.encrypt(<span class="name">JSON</span>.stringify(<span class="name">param</span>))<span class="comment">;</span></span><br><span class="line">            return AES.encrypt(<span class="name">param</span>, aes_client_key, aes_client_iv)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)()<span class="comment">;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到这里使用了 Base64 和 AES 加密。加密之后的字符串便作为 POST Data 传送给服务器了，然后服务器再进行解密处理，然后进行逻辑处理，然后再对处理后的数据进行加密，返回了加密后的数据，那么 JavaScript 再接收到之后再进行一次解密，再渲染才能得到正常的结果。 所以这里还需要分析服务器传回的数据是怎样解密的。顺腾摸瓜，很容易就找到一个 decodeData() 方法，其定义如下：</p>
                  <figure class="highlight fortran">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">function</span></span> decodeData(<span class="keyword">data</span>) &#123;</span><br><span class="line">        <span class="keyword">data</span> = AES.decrypt(<span class="keyword">data</span>, aes_server_key, aes_server_iv);</span><br><span class="line">        <span class="keyword">data</span> = DES.decrypt(<span class="keyword">data</span>, des_key, des_iv);</span><br><span class="line">        <span class="keyword">data</span> = BASE64.decrypt(<span class="keyword">data</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">data</span></span><br><span class="line">    &#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>嗯，这里又经过了三层解密，才把正常的明文数据解析出来。 所以一切都清晰了，我们需要实现两个过程才能正常使用这个接口，即实现 POST Data 的加密过程和 Response Data 的解密过程。其中 POST Data 的加密过程是 Base64 + AES 加密，Response Data 的解密是 AES + DES + Base64 解密。加密解密的 Key 也都在 JavaScript 文件里能找到，我们用 Python 实现这些加密解密过程就可以了。 所以接下来怎么办？接着刚啊！ 接着刚才怪！ 何必去费那些事去用 Python 重写一遍 JavaScript，万一二者里面有数据格式不统一或者二者由于语言不兼容问题导致计算结果偏差，上哪里去 Debug？ 那怎么办？这里我们借助于 PyExecJS 库来实现 JavaScript 模拟就好了。</p>
                  <h2 id="PyExecJS"><a href="#PyExecJS" class="headerlink" title="PyExecJS"></a>PyExecJS</h2>
                  <p>PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库。大家可能听说过 PyV8，它也是用来模拟执行 JavaScript 的库，可是由于这个项目已经不维护了，而且对 Python3 的支持不好，而且安装出现各种问题，所以这里选用了 PyExecJS 库来代替它。 首先我们来安装一下这个库：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip <span class="keyword">install</span> PyExecJS</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>使用 pip 安装即可。 在使用这个库之前请确保你的机器上安装了以下其中一个JS运行环境:</p>
                  <ul>
                    <li>JScript</li>
                    <li>JavaScriptCore</li>
                    <li>Nashorn</li>
                    <li>Node</li>
                    <li>PhantomJS</li>
                    <li>PyV8</li>
                    <li>SlimerJS</li>
                    <li>SpiderMonkey</li>
                  </ul>
                  <p>PyExecJS 库会按照优先级调用这些引擎来实现 JavaScript 执行，这里推荐安装 Node.js 或 PhantomJS。 接着我们运行代码检查一下运行环境：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import execjs</span><br><span class="line"><span class="builtin-name">print</span>(execjs.<span class="builtin-name">get</span>().name)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行之后，由于我安装了 Node.js，所以这里会使用 Node.js 作为渲染引擎，结果如下：</p>
                  <figure class="highlight crmsh">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">Node</span>.<span class="title">js</span> (V8)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>接下来我们将刚才反混淆的 JavaScript 保存成一个文件，叫做 encryption.js，然后用 PyExecJS 模拟运行相关的方法即可。 首先我们来实现加密过程，这里 getServerData() 方法其实已经帮我们实现好了，并实现了 Ajax 请求，但这个方法里面有获取 Storage 的方法，Node.js 不适用，所以这里我们直接改写下，实现一个 getEncryptedData() 方法实现加密，在 encryption.js 里面实现如下方法：</p>
                  <figure class="highlight oxygene">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getEncryptedData</span><span class="params">(<span class="keyword">method</span>, city, <span class="keyword">type</span>, startTime, endTime)</span> <span class="comment">&#123;</span></span></span><br><span class="line"><span class="function"><span class="comment">    var param = &#123;&#125;</span>;</span></span><br><span class="line">    param.city = city;</span><br><span class="line">    param.type = <span class="keyword">type</span>;</span><br><span class="line">    param.startTime = startTime;</span><br><span class="line">    param.endTime = endTime;</span><br><span class="line">    return getParam(<span class="function"><span class="keyword">method</span>, <span class="title">param</span>);</span></span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>接着我们模拟执行这些方法即可：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import execjs</span><br><span class="line"></span><br><span class="line"><span class="comment"># Init environment</span></span><br><span class="line">node = execjs.<span class="builtin-name">get</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Params</span></span><br><span class="line">method = <span class="string">'GETCITYWEATHER'</span></span><br><span class="line">city = <span class="string">'北京'</span></span><br><span class="line">type = <span class="string">'HOUR'</span></span><br><span class="line">start_time = <span class="string">'2018-01-25 00:00:00'</span></span><br><span class="line">end_time = <span class="string">'2018-01-25 23:00:00'</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Compile javascript</span></span><br><span class="line">file = <span class="string">'encryption.js'</span></span><br><span class="line">ctx = node.compile(open(file).read())</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get params</span></span><br><span class="line">js = <span class="string">'getEncryptedData("&#123;0&#125;", "&#123;1&#125;", "&#123;2&#125;", "&#123;3&#125;", "&#123;4&#125;")'</span>.format(method, city, type, start_time, end_time)</span><br><span class="line">params = ctx.eval(js)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们首先定义一些参数，如 method、city、start_time 等，这些都可以通过分析 JavaScript 很容易得出其规则。 然后这里首先通过 execjs（即 PyExecJS）的 get() 方法声明一个运行环境，然后调用 compile() 方法来执行刚才保存下来的加密库 encryption.js，因为这里面包含了一些加密方法和自定义方法，所以只有执行一遍才能调用。 接着我们再构造一个 js 字符串，传递这些参数，然后通过 eval() 方法来模拟执行，得到的结果赋值为 params，这个就是 POST Data 的加密数据。 接着我们直接用 requests 库来模拟 POST 请求就好了，也没必要用 jQuery 自带的 Ajax 了，当然后者也是可行的，只不过需要加载一下 jQuery 库。 接着我们用 requests 库来模拟 POST 请求：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"># <span class="keyword">Get</span> <span class="keyword">encrypted</span> response <span class="type">text</span></span><br><span class="line">api = <span class="string">'https://www.aqistudy.cn/apinew/aqistudyapi.php'</span></span><br><span class="line">response = requests.post(api, data=&#123;<span class="string">'d'</span>: params&#125;)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样 response 的内容就是服务器返回的加密的内容了。 接下来我们再调用一下 JavaScript 中的 decodeData() 方法即可实现解密：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment"># Decode data</span></span><br><span class="line"><span class="attr">js</span> = <span class="string">'decodeData("&#123;0&#125;")'</span>.format(response.text)</span><br><span class="line"><span class="attr">decrypted_data</span> = ctx.eval(js)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样 decrypted_data 就是解密后的字符串了，解密之后，实际上是一个 JSON 字符串：</p>
                  <figure class="highlight 1c">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;'success': True, 'errcode': <span class="number">0</span>, 'errmsg': 'success', 'result': &#123;'success': True, 'data': &#123;'total': <span class="number">22</span>, 'rows': [&#123;'time': '<span class="number">2018-01-25</span> 00:00:00', 'temp': '-7', 'humi': '35', 'wse': '1', 'wd': '东北风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 01:00:00', 'temp': '-9', 'humi': '38', 'wse': '1', 'wd': '西风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 02:00:00', 'temp': '-10', 'humi': '40', 'wse': '1', 'wd': '东北风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 03:00:00', 'temp': '-8', 'humi': '27', 'wse': '2', 'wd': '东北风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 04:00:00', 'temp': '-8', 'humi': '26', 'wse': '2', 'wd': '东风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 05:00:00', 'temp': '-8', 'humi': '23', 'wse': '2', 'wd': '东北风', 'tq': '晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 06:00:00', 'temp': '-9', 'humi': '27', 'wse': '2', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 07:00:00', 'temp': '-9', 'humi': '24', 'wse': '2', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 08:00:00', 'temp': '-9', 'humi': '25', 'wse': '2', 'wd': '东风', 'tq': '晴转多云转多云间晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 09:00:00', 'temp': '-8', 'humi': '21', 'wse': '3', 'wd': '东北风', 'tq': '晴转多云转多云间晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 10:00:00', 'temp': '-7', 'humi': '19', 'wse': '3', 'wd': '东北风', 'tq': '晴转多云转多云间晴'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 11:00:00', 'temp': '-6', 'humi': '18', 'wse': '3', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 12:00:00', 'temp': '-6', 'humi': '17', 'wse': '3', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 13:00:00', 'temp': '-5', 'humi': '17', 'wse': '2', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 14:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 15:00:00', 'temp': '-5', 'humi': '15', 'wse': '2', 'wd': '北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 16:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东北风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 17:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东风', 'tq': '多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 18:00:00', 'temp': '-6', 'humi': '18', 'wse': '2', 'wd': '东风', 'tq': '晴间多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 19:00:00', 'temp': '-7', 'humi': '19', 'wse': '2', 'wd': '东风', 'tq': '晴间多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 20:00:00', 'temp': '-7', 'humi': '19', 'wse': '1', 'wd': '东风', 'tq': '晴间多云'&#125;, &#123;'time': '<span class="number">2018-01-25</span> 21:00:00', 'temp': '-7', 'humi': '19', 'wse': '0', 'wd': '南风', 'tq': '晴间多云'&#125;]&#125;&#125;&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>大功告成！ 这样我们就可以成功获取温度、湿度、风力、天气等信息了。 另外这部分数据其实不全，还有 PM 2.5、AQI 等数据需要用另外一个 method 参数 GETDETAIL，修改一下即可获取这部分数据了。 再往后的数据就是解析和存储了，这里不再赘述。</p>
                  <h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2>
                  <p>本文通过分析 JavaScript 并进行反混淆，然后用 Python 模拟运行 JavaScript 的方式实现了数据抓取。 代码地址：<a href="https://github.com/Germey/AQIStudy" target="_blank" rel="noopener">https://github.com/Germey/AQIStudy</a>。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-26 00:06:49" itemprop="dateCreated datePublished" datetime="2018-01-26T00:06:49+08:00">2018-01-26</time>
                </span>
                <span id="/5024.html" class="post-meta-item leancloud_visitors" data-flag-title="JavaScript加密逻辑分析与Python模拟执行实现数据爬取" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>7.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>7 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5459.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5459.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.6-Gerapy的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Gerapy是一个Scrapy分布式管理模块，本节就来介绍一下它的安装方式。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://github.com/Gerapy" target="_blank" rel="noopener">https://github.com/Gerapy</a></li>
                  </ul>
                  <h2 id="2-pip安装"><a href="#2-pip安装" class="headerlink" title="2. pip安装"></a>2. pip安装</h2>
                  <p>这里推荐使用pip安装，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> gerapy</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="3-测试安装"><a href="#3-测试安装" class="headerlink" title="3. 测试安装"></a>3. 测试安装</h2>
                  <p>安装完成后，可以在Python命令行下测试：</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">$ python3</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; import gerapy</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果没有错误报出，则证明库已经安装好了。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:58:22" itemprop="dateCreated datePublished" datetime="2018-01-25T23:58:22+08:00">2018-01-25</time>
                </span>
                <span id="/5459.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.6-Gerapy的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>200</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5456.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5456.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.5-Scrapyrt的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Scrapyrt为Scrapy提供了一个调度的HTTP接口，有了它，我们就不需要再执行Scrapy命令而是通过请求一个HTTP接口来调度Scrapy任务了。Scrapyrt比Scrapyd更轻量，如果不需要分布式多任务的话，可以简单使用Scrapyrt实现远程Scrapy任务的调度。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://github.com/scrapinghub/scrapyrt" target="_blank" rel="noopener">https://github.com/scrapinghub/scrapyrt</a></li>
                    <li>官方文档：<a href="http://scrapyrt.readthedocs.io/" target="_blank" rel="noopener">http://scrapyrt.readthedocs.io</a></li>
                  </ul>
                  <h2 id="2-pip安装"><a href="#2-pip安装" class="headerlink" title="2. pip安装"></a>2. pip安装</h2>
                  <p>这里推荐使用pip安装，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> scrapyrt</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>接下来，在任意一个Scrapy项目中运行如下命令来启动HTTP服务：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">scrapyrt</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行之后，会默认在9080端口上启动服务，类似的输出结果如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">scrapyrt</span><br><span class="line"><span class="number">2017</span><span class="number">-07</span><span class="number">-12</span> <span class="number">22</span>:<span class="number">31</span>:<span class="number">03</span>+<span class="number">0800</span> [-] Log opened.</span><br><span class="line"><span class="number">2017</span><span class="number">-07</span><span class="number">-12</span> <span class="number">22</span>:<span class="number">31</span>:<span class="number">03</span>+<span class="number">0800</span> [-] Site starting on <span class="number">9080</span></span><br><span class="line"><span class="number">2017</span><span class="number">-07</span><span class="number">-12</span> <span class="number">22</span>:<span class="number">31</span>:<span class="number">03</span>+<span class="number">0800</span> [-] Starting factory &lt;twisted.web.server.Site object at <span class="number">0x10294b160</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果想更换运行端口，可以使用<code>\-p</code>参数，如：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">scrapyrt -p <span class="number">9081</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样就会在9081端口上运行了。</p>
                  <h2 id="3-Docker安装"><a href="#3-Docker安装" class="headerlink" title="3. Docker安装"></a>3. Docker安装</h2>
                  <p>另外，Scrapyrt也支持Docker。比如，要想在9080端口上运行，且本地Scrapy项目的路径为/home/quotesbot，可以使用如下命令运行：</p>
                  <figure class="highlight awk">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">docker run -p <span class="number">9080</span>:<span class="number">9080</span> -tid -v <span class="regexp">/home/u</span>ser<span class="regexp">/quotesbot:/</span>scrapyrt<span class="regexp">/project scrapinghub/</span>scrapyrt</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样同样可以在9080端口上监听指定的Scrapy项目。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:56:01" itemprop="dateCreated datePublished" datetime="2018-01-25T23:56:01+08:00">2018-01-25</time>
                </span>
                <span id="/5456.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.5-Scrapyrt的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>785</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5453.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5453.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.4-Scrapyd API的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>安装好了Scrapyd之后，我们可以直接请求它提供的API来获取当前主机的Scrapy任务运行状况。比如，某台主机的IP为192.168.1.1，则可以直接运行如下命令获取当前主机的所有Scrapy项目：</p>
                  <figure class="highlight groovy">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">curl <span class="string">http:</span><span class="comment">//localhost:6800/listprojects.json</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;<span class="attr">"status"</span>: <span class="string">"ok"</span>, <span class="attr">"projects"</span>: [<span class="string">"myproject"</span>, <span class="string">"otherproject"</span>]&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>返回结果是JSON字符串，通过解析这个字符串，便可以得到当前主机的所有项目。</p>
                  <p>但是用这种方式来获取任务状态还是有点烦琐，所以Scrapyd API就为它做了一层封装，下面我们来看下它的安装方式。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://pypi.python.org/pypi/python-scrapyd-api/" target="_blank" rel="noopener">https://pypi.python.org/pypi/python-scrapyd-api/</a></li>
                    <li>PyPI：<a href="https://pypi.python.org/pypi/python-scrapyd-api" target="_blank" rel="noopener">https://pypi.python.org/pypi/python-scrapyd-api</a></li>
                    <li>官方文档：<a href="http://python-scrapyd-api.readthedocs.io/en/latest/usage.html" target="_blank" rel="noopener">http://python-scrapyd-api.readthedocs.io/en/latest/usage.html</a></li>
                  </ul>
                  <h2 id="2-pip安装"><a href="#2-pip安装" class="headerlink" title="2. pip安装"></a>2. pip安装</h2>
                  <p>这里推荐使用pip安装，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip <span class="keyword">install</span> python-scrapyd-api</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="3-验证安装"><a href="#3-验证安装" class="headerlink" title="3. 验证安装"></a>3. 验证安装</h2>
                  <p>安装完成之后，便可以使用Python来获取主机状态了，所以上面的操作便可以用Python代码实现：</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from scrapyd_api import ScrapydAPI</span><br><span class="line">scrapyd = ScrapydAPI('http<span class="symbol">://localhost</span>:<span class="number">6800</span>')</span><br><span class="line">print(<span class="name">scrapyd</span>.list_projects())</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="string">"myproject"</span>, <span class="string">"otherproject"</span>]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样我们便可以用Python直接来获取各个主机上Scrapy任务的运行状态了。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:53:01" itemprop="dateCreated datePublished" datetime="2018-01-25T23:53:01+08:00">2018-01-25</time>
                </span>
                <span id="/5453.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.4-Scrapyd API的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>783</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5449.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5449.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.3-Scrapyd-Client的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在将Scrapy代码部署到远程Scrapyd的时候，第一步就是要将代码打包为EGG文件，其次需要将EGG文件上传到远程主机。这个过程如果用程序来实现，也是完全可以的，但是我们并不需要做这些工作，因为Scrapyd-Client已经为我们实现了这些功能。</p>
                  <p>下面我们就来看看Scrapyd-Client的安装过程。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://github.com/scrapy/scrapyd-client" target="_blank" rel="noopener">https://github.com/scrapy/scrapyd-client</a></li>
                    <li>PyPI：<a href="https://pypi.python.org/pypi/scrapyd-client" target="_blank" rel="noopener">https://pypi.python.org/pypi/scrapyd-client</a></li>
                    <li>使用说明：<a href="https://github.com/scrapy/scrapyd-client#scrapyd-deploy" target="_blank" rel="noopener">https://github.com/scrapy/scrapyd-client#scrapyd-deploy</a></li>
                  </ul>
                  <h2 id="2-pip安装"><a href="#2-pip安装" class="headerlink" title="2. pip安装"></a>2. pip安装</h2>
                  <p>这里推荐使用pip安装，相关命令如下：</p>
                  <figure class="highlight sql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> scrapyd-<span class="keyword">client</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="3-验证安装"><a href="#3-验证安装" class="headerlink" title="3. 验证安装"></a>3. 验证安装</h2>
                  <p>安装成功后会有一个可用命令，叫作scrapyd-deploy，即部署命令。</p>
                  <p>我们可以输入如下测试命令测试Scrapyd-Client是否安装成功：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">scrapyd-deploy -h</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果出现类似如图1-86所示的输出，则证明Scrapyd-Client已经成功安装。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-87-1.jpg" alt="">图1-86 运行结果</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:50:01" itemprop="dateCreated datePublished" datetime="2018-01-25T23:50:01+08:00">2018-01-25</time>
                </span>
                <span id="/5449.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.3-Scrapyd-Client的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>513</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5445.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5445.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.2-Scrapyd的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Scrapyd是一个用于部署和运行Scrapy项目的工具，有了它，你可以将写好的Scrapy项目上传到云主机并通过API来控制它的运行。</p>
                  <p>既然是Scrapy项目部署，基本上都使用Linux主机，所以本节的安装是针对于Linux主机的。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://github.com/scrapy/scrapyd" target="_blank" rel="noopener">https://github.com/scrapy/scrapyd</a></li>
                    <li>PyPI：<a href="https://pypi.python.org/pypi/scrapyd" target="_blank" rel="noopener">https://pypi.python.org/pypi/scrapyd</a></li>
                    <li>官方文档：<a href="https://scrapyd.readthedocs.io/" target="_blank" rel="noopener">https://scrapyd.readthedocs.io</a></li>
                  </ul>
                  <h2 id="2-pip安装"><a href="#2-pip安装" class="headerlink" title="2. pip安装"></a>2. pip安装</h2>
                  <p>这里推荐使用pip安装，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> scrapyd</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="3-配置"><a href="#3-配置" class="headerlink" title="3. 配置"></a>3. 配置</h2>
                  <p>安装完毕之后，需要新建一个配置文件/etc/scrapyd/scrapyd.conf，Scrapyd在运行的时候会读取此配置文件。</p>
                  <p>在Scrapyd 1.2版本之后，不会自动创建该文件，需要我们自行添加。</p>
                  <p>首先，执行如下命令新建文件：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">sudo <span class="built_in">mkdir</span> /etc/scrapyd</span><br><span class="line">sudo <span class="keyword">vi</span> /etc/scrapyd/scrapyd.<span class="keyword">conf</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>接着写入如下内容：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="section">[scrapyd]</span></span><br><span class="line"><span class="attr">eggs_dir</span>    = eggs</span><br><span class="line"><span class="attr">logs_dir</span>    = logs</span><br><span class="line"><span class="attr">items_dir</span>   =</span><br><span class="line"><span class="attr">jobs_to_keep</span> = <span class="number">5</span></span><br><span class="line"><span class="attr">dbs_dir</span>     = dbs</span><br><span class="line"><span class="attr">max_proc</span>    = <span class="number">0</span></span><br><span class="line"><span class="attr">max_proc_per_cpu</span> = <span class="number">10</span></span><br><span class="line"><span class="attr">finished_to_keep</span> = <span class="number">100</span></span><br><span class="line"><span class="attr">poll_interval</span> = <span class="number">5.0</span></span><br><span class="line"><span class="attr">bind_address</span> = <span class="number">0.0</span>.<span class="number">0.0</span></span><br><span class="line"><span class="attr">http_port</span>   = <span class="number">6800</span></span><br><span class="line"><span class="attr">debug</span>       = <span class="literal">off</span></span><br><span class="line"><span class="attr">runner</span>      = scrapyd.runner</span><br><span class="line"><span class="attr">application</span> = scrapyd.app.application</span><br><span class="line"><span class="attr">launcher</span>    = scrapyd.launcher.Launcher</span><br><span class="line"><span class="attr">webroot</span>     = scrapyd.website.Root</span><br><span class="line"></span><br><span class="line"><span class="section">[services]</span></span><br><span class="line"><span class="attr">schedule.json</span>     = scrapyd.webservice.Schedule</span><br><span class="line"><span class="attr">cancel.json</span>       = scrapyd.webservice.Cancel</span><br><span class="line"><span class="attr">addversion.json</span>   = scrapyd.webservice.AddVersion</span><br><span class="line"><span class="attr">listprojects.json</span> = scrapyd.webservice.ListProjects</span><br><span class="line"><span class="attr">listversions.json</span> = scrapyd.webservice.ListVersions</span><br><span class="line"><span class="attr">listspiders.json</span>  = scrapyd.webservice.ListSpiders</span><br><span class="line"><span class="attr">delproject.json</span>   = scrapyd.webservice.DeleteProject</span><br><span class="line"><span class="attr">delversion.json</span>   = scrapyd.webservice.DeleteVersion</span><br><span class="line"><span class="attr">listjobs.json</span>     = scrapyd.webservice.ListJobs</span><br><span class="line"><span class="attr">daemonstatus.json</span> = scrapyd.webservice.DaemonStatus</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>配置文件的内容可以参见官方文档<a href="https://scrapyd.readthedocs.io/en/stable/config.html#example-configuration-file" target="_blank" rel="noopener">https://scrapyd.readthedocs.io/en/stable/config.html#example-configuration-file</a>。这里的配置文件有所修改，其中之一是<code>max_proc_per_cpu</code>官方默认为4，即一台主机每个CPU最多运行4个Scrapy任务，在此提高为10。另外一个是<code>bind_address</code>，默认为本地127.0.0.1，在此修改为0.0.0.0，以使外网可以访问。</p>
                  <h2 id="4-后台运行"><a href="#4-后台运行" class="headerlink" title="4. 后台运行"></a>4. 后台运行</h2>
                  <p>Scrapyd是一个纯Python项目，这里可以直接调用它来运行。为了使程序一直在后台运行，Linux和Mac可以使用如下命令：</p>
                  <figure class="highlight 1c">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">(scrapyd &gt; /dev/<span class="literal">null</span> <span class="meta">&amp;)</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样Scrapyd就会在后台持续运行了，控制台输出直接忽略。当然，如果想记录输出日志，可以修改输出目标，如：</p>
                  <figure class="highlight clojure">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">(<span class="name">scrapyd</span> &gt; ~/scrapyd.log &amp;)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>此时会将Scrapyd的运行结果输出到~/scrapyd.log文件中。</p>
                  <p>当然也可以使用screen、tmux、supervisor等工具来实现进程守护。</p>
                  <p>运行之后，便可以在浏览器的6800端口访问Web UI了，从中可以看到当前Scrapyd的运行任务、日志等内容，如图1-85所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-86.png" alt="">图1-85 Scrapyd首页</p>
                  <p>当然，运行Scrapyd更佳的方式是使用Supervisor守护进程，如果感兴趣，可以参考：<a href="http://supervisord.org/" target="_blank" rel="noopener">http://supervisord.org/</a>。</p>
                  <p>另外，Scrapyd也支持Docker，后面我们会介绍Scrapyd Docker镜像的制作和运行方法。</p>
                  <h2 id="5-访问认证"><a href="#5-访问认证" class="headerlink" title="5. 访问认证"></a>5. 访问认证</h2>
                  <p>配置完成后，Scrapyd和它的接口都是可以公开访问的。如果想配置访问认证的话，可以借助于Nginx做反向代理，这里需要先安装Nginx服务器。</p>
                  <p>在此以Ubuntu为例进行说明，安装命令如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">sudo apt-<span class="builtin-name">get</span> install nginx</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>然后修改Nginx的配置文件nginx.conf，增加如下配置：</p>
                  <figure class="highlight nginx">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="section">http</span> &#123;</span><br><span class="line">    <span class="section">server</span> &#123;</span><br><span class="line">        <span class="attribute">listen</span> <span class="number">6801</span>;</span><br><span class="line">        <span class="attribute">location</span> / &#123;</span><br><span class="line">            <span class="attribute">proxy_pass</span>    http://127.0.0.1:6800/;</span><br><span class="line">            <span class="attribute">auth_basic</span>    <span class="string">"Restricted"</span>;</span><br><span class="line">            <span class="attribute">auth_basic_user_file</span>    /etc/nginx/conf.d/.htpasswd;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里使用的用户名和密码配置放置在/etc/nginx/conf.d目录下，我们需要使用<code>htpasswd</code>命令创建。例如，创建一个用户名为admin的文件，命令如下：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">htpasswd</span> <span class="selector-tag">-c</span> <span class="selector-class">.htpasswd</span> <span class="selector-tag">admin</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>接着就会提示我们输入密码，输入两次之后，就会生成密码文件。此时查看这个文件的内容：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">cat</span> <span class="selector-class">.htpasswd</span> </span><br><span class="line"><span class="selector-tag">admin</span><span class="selector-pseudo">:5ZBxQr0rCqwbc</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>配置完成后，重启一下Nginx服务，运行如下命令：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">sudo nginx -s reload</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样就成功配置了Scrapyd的访问认证了。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:45:46" itemprop="dateCreated datePublished" datetime="2018-01-25T23:45:46+08:00">2018-01-25</time>
                </span>
                <span id="/5445.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.2-Scrapyd的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.5k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5438.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5438.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9.1-Docker的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Docker是一种容器技术，可以将应用和环境等进行打包，形成一个独立的、类似于iOS的App形式的“应用”。这个应用可以直接被分发到任意一个支持Docker的环境中，通过简单的命令即可启动运行。Docker是一种最流行的容器化实现方案，和虚拟化技术类似，它极大地方便了应用服务的部署；又与虚拟化技术不同，它以一种更轻量的方式实现了应用服务的打包。使用Docker，可以让每个应用彼此相互隔离，在同一台机器上同时运行多个应用，不过它们彼此之间共享同一个操作系统。Docker的优势在于，它可以在更细的粒度上进行资源管理，也比虚拟化技术更加节约资源。</p>
                  <p>对于爬虫来说，如果我们需要大规模部署爬虫系统的话，用Docker会大大提高效率。工欲善其事，必先利其器。</p>
                  <p>本节中，我们就来介绍三大平台下Docker的安装方式。</p>
                  <h2 id="1-相关链接"><a href="#1-相关链接" class="headerlink" title="1. 相关链接"></a>1. 相关链接</h2>
                  <ul>
                    <li>官方网站：<a href="https://www.docker.com/" target="_blank" rel="noopener">https://www.docker.com</a></li>
                    <li>GitHub：<a href="https://github.com/docker" target="_blank" rel="noopener">https://github.com/docker</a></li>
                    <li>Docker Hub：<a href="https://hub.docker.com/" target="_blank" rel="noopener">https://hub.docker.com</a></li>
                    <li>官方文档：<a href="https://docs.docker.com/" target="_blank" rel="noopener">https://docs.docker.com</a></li>
                    <li>DaoCloud：<a href="http://www.daocloud.io/" target="_blank" rel="noopener">http://www.daocloud.io</a></li>
                    <li>中文社区：<a href="http://www.docker.org.cn/" target="_blank" rel="noopener">http://www.docker.org.cn</a></li>
                    <li>中文教程：<a href="http://www.runoob.com/docker/docker-tutorial.html" target="_blank" rel="noopener">http://www.runoob.com/docker/docker-tutorial.html</a></li>
                    <li>推荐图书：<a href="https://yeasy.gitbooks.io/docker_practice" target="_blank" rel="noopener">https://yeasy.gitbooks.io/docker_practice</a></li>
                  </ul>
                  <h2 id="2-Windows下的安装"><a href="#2-Windows下的安装" class="headerlink" title="2. Windows下的安装"></a>2. Windows下的安装</h2>
                  <p>如果你的系统是Windows 10 64位，那么推荐使用Docker for Windows。此时直接从Docker官方网站下载最新的Docker for Windows 安装包即可：<a href="https://docs.docker.com/docker-for-windows/install/" target="_blank" rel="noopener">https://docs.docker.com/docker-for-windows/install/</a>。</p>
                  <p>如果不是Windows 10 64位系统，则可以下载Docker Toolbox：<a href="https://docs.docker.com/toolbox/toolbox_install_windows/" target="_blank" rel="noopener">https://docs.docker.com/toolbox/toolbox_install_windows/</a>。</p>
                  <p>下载后直接双击安装即可，详细过程可以参考文档说明。安装完成后，进入命令行。</p>
                  <p>运行<code>docker</code>命令测试：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">docker</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如图1-81所示，这就证明Docker安装成功了。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-82.jpg" alt="">图1-81 运行结果</p>
                  <h2 id="3-Linux下的安装"><a href="#3-Linux下的安装" class="headerlink" title="3. Linux下的安装"></a>3. Linux下的安装</h2>
                  <p>详细的分步骤安装说明可以参见官方文档：<a href="https://docs.docker.com/engine/installation/linux/ubuntu/" target="_blank" rel="noopener">https://docs.docker.com/engine/installation/linux/ubuntu/</a>。</p>
                  <p>官方文档中详细说明了不同Linux系统的安装方法，根据文档一步步执行即可安装成功。但是为了使安装更加方便，Docker官方还提供了一键安装脚本。使用它，会使安装更加便捷，不用再去一步步执行命令安装了。</p>
                  <p>首先是Docker官方提供的安装脚本。相比其他脚本，官方提供的一定更靠谱，安装命令如下：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">curl -sSL http<span class="variable">s:</span>//<span class="built_in">get</span>.docker.<span class="keyword">com</span>/ | <span class="keyword">sh</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>只要执行如上一条命令，等待一会儿Docker便会安装完成，这非常方便。</p>
                  <p>但是使用官方脚本安装有一个缺点，那就是慢，也可能下载超时，所以为了加快下载速度，我们可以使用国内的镜像来安装，所以这里还有阿里云和DaoCloud的安装脚本。</p>
                  <p>阿里云的安装脚本：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">curl -sSL http://acs-public-mirror.oss-<span class="keyword">cn</span>-hangzhou.aliyuncs.<span class="keyword">com</span>/docker-engine/internet | <span class="keyword">sh</span> -</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>DaoCloud的安装脚本：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">curl -sSL http<span class="variable">s:</span>//<span class="built_in">get</span>.daocloud.io/docker | <span class="keyword">sh</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这两个脚本可以任选其一，速度都非常不错。</p>
                  <p>等待脚本执行完毕之后，就可以使用Docker相关命令了，如运行测试Hello World镜像：</p>
                  <figure class="highlight dockerfile">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">docker <span class="keyword">run</span><span class="bash"> hello-world</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Unable <span class="keyword">to</span> <span class="keyword">find</span> image <span class="string">'hello-world:latest'</span> locally</span><br><span class="line">lates<span class="variable">t:</span> Pulling from library/hello-world</span><br><span class="line"><span class="number">78445</span>dd45222: Pull <span class="built_in">complete</span> </span><br><span class="line">Diges<span class="variable">t:</span> <span class="built_in">sha256</span>:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7</span><br><span class="line">Statu<span class="variable">s:</span> Downloaded newer image <span class="keyword">for</span> hello-world:latest</span><br><span class="line">Hello from Docker!</span><br><span class="line">This message shows that your installation appears <span class="keyword">to</span> <span class="keyword">be</span> working correctly.</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果出现类似上面提示的内容，则证明Docker可以正常使用了。</p>
                  <h2 id="4-Mac下的安装"><a href="#4-Mac下的安装" class="headerlink" title="4. Mac下的安装"></a>4. Mac下的安装</h2>
                  <p>Mac平台同样有两种选择：Docker for Mac和Docker Toolbox。</p>
                  <p>Docker for Mac要求系统为OS X EI Captain 10.11或更新，至少4GB内存。如果你的系统满足此要求，则强烈建议安装Docker for Mac。</p>
                  <p>这里可以使用Homebrew安装，安装命令如下：</p>
                  <figure class="highlight mipsasm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">brew </span>cask <span class="keyword">install </span>docker</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>另外，也可以手动下载安装包（下载地址为：<a href="https://download.docker.com/mac/stable/Docker.dmg" target="_blank" rel="noopener">https://download.docker.com/mac/stable/Docker.dmg</a>）安装。</p>
                  <p>下载完成后，直接双击安装包，然后将程序拖动到应用程序中即可。</p>
                  <p>点击程序图标运行Docker，会发现在菜单栏中出现了Docker的图标，如图1-82中的第三个小鲸鱼图标。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-83.jpg" alt="">图1-82 菜单栏</p>
                  <p>点击小鲸鱼图标，展开菜单之后，再点击Start按钮即可启动Docker。启动成功后，便会提示Docker is running，如图1-83所示。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-84.jpg" alt="">图1-83 运行页面</p>
                  <p>随后，我们就可以在命令行下使用Docker命令了。</p>
                  <p>可以使用如下命令测试运行：</p>
                  <figure class="highlight dockerfile">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">sudo docker <span class="keyword">run</span><span class="bash"> hello-world</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如图1-84所示，这就证明Docker已经成功安装了。</p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/1-85.jpg" alt="">图1-84 运行结果</p>
                  <p>如果系统不满足要求，可以下载Docker Toolbox，其安装说明为：<a href="https://docs.docker.com/toolbox/overview/" target="_blank" rel="noopener">https://docs.docker.com/toolbox/overview/</a>。</p>
                  <p>关于Docker for Mac和Docker Toolbox的区别，可以参见：<a href="https://docs.docker.com/docker-for-mac/docker-toolbox/" target="_blank" rel="noopener">https://docs.docker.com/docker-for-mac/docker-toolbox/</a>。</p>
                  <h2 id="5-镜像加速"><a href="#5-镜像加速" class="headerlink" title="5. 镜像加速"></a>5. 镜像加速</h2>
                  <p>安装好Docker之后，在运行测试命令时，我们会发现它首先会下载一个Hello World的镜像，然后将其运行。但是这里的下载速度有时候会非常慢，这是因为它默认还是从国外的Docker Hub下载的。因此，为了提高镜像的下载速度，我们还可以使用国内镜像来加速下载，于是就有了Docker加速器一说。</p>
                  <p>推荐的Docker加速器有DaoCloud（详见<a href="https://www.daocloud.io/mirror" target="_blank" rel="noopener">https://www.daocloud.io/mirror</a>）和阿里云（详见<a href="https://cr.console.aliyun.com/#/accelerator" target="_blank" rel="noopener">https://cr.console.aliyun.com/#/accelerator</a>）。</p>
                  <p>不同平台的镜像加速方法配置可以参考DaoCloud的官方文档：<a href="http://guide.daocloud.io/dcs/daocloud-9153151.html" target="_blank" rel="noopener">http://guide.daocloud.io/dcs/daocloud-9153151.html</a>。</p>
                  <p>配置完成之后，可以发现镜像的下载速度会快非常多。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:38:30" itemprop="dateCreated datePublished" datetime="2018-01-25T23:38:30+08:00">2018-01-25</time>
                </span>
                <span id="/5438.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9.1-Docker的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>3k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>3 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5435.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5435.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.9-部署相关库的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>如果想要大规模抓取数据，那么一定会用到分布式爬虫。对于分布式爬虫来说，我们需要多台主机，每台主机多个爬虫任务，但是源代码其实只有一份。此时我们需要做的就是将一份代码同时部署到多台主机上来协同运行，那么怎么去部署就是另一个值得思考的问题。</p>
                  <p>对于Scrapy来说，它有一个扩展组件，叫作Scrapyd，我们只需要安装该扩展组件，即可远程管理Scrapy任务，包括部署源码、启动任务、监听任务等。另外，还有Scrapyd-Client和Scrapyd API来帮助我们更方便地完成部署和监听操作。</p>
                  <p>另外，还有一种部署方式，那就是Docker集群部署。我们只需要将爬虫制作为Docker镜像，只要主机安装了Docker，就可以直接运行爬虫，而无需再去担心环境配置、版本问题。</p>
                  <p>本节中，我们就来介绍相关环境的配置过程。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:36:04" itemprop="dateCreated datePublished" datetime="2018-01-25T23:36:04+08:00">2018-01-25</time>
                </span>
                <span id="/5435.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.9-部署相关库的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>350</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/5432.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/5432.html" class="post-title-link" itemprop="url">[Python3网络爬虫开发实战] 1.8.4-Scrapy-Redis的安装</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Scrapy-Redis是Scrapy的分布式扩展模块，有了它，我们就可以方便地实现Scrapy分布式爬虫的搭建。本节中，我们将介绍Scrapy-Redis的安装方式。</p>
                  <h2 id="相关链接"><a href="#相关链接" class="headerlink" title="相关链接"></a>相关链接</h2>
                  <ul>
                    <li>GitHub：<a href="https://github.com/rmax/scrapy-redis" target="_blank" rel="noopener">https://github.com/rmax/scrapy-redis</a></li>
                    <li>PyPI：<a href="https://pypi.python.org/pypi/scrapy-redis" target="_blank" rel="noopener">https://pypi.python.org/pypi/scrapy-redis</a></li>
                    <li>官方文档：<a href="http://scrapy-redis.readthedocs.io/" target="_blank" rel="noopener">http://scrapy-redis.readthedocs.io</a></li>
                  </ul>
                  <h2 id="pip安装"><a href="#pip安装" class="headerlink" title="pip安装"></a>pip安装</h2>
                  <p>这里推荐使用pip安装，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> scrapy-redis</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="wheel安装"><a href="#wheel安装" class="headerlink" title="wheel安装"></a>wheel安装</h2>
                  <p>此外，也可以到PyPI下载wheel文件安装（详见<a href="https://pypi.python.org/pypi/scrapy-redis#downloads" target="_blank" rel="noopener">https://pypi.python.org/pypi/scrapy-redis#downloads</a>），如当前的最新版本为0.6.8，则可以下载scrapy_redis-0.6.8-py2.py3-none-any.whl，然后通过pip安装即可：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">pip3</span> <span class="selector-tag">install</span> <span class="selector-tag">scrapy_redis-0</span><span class="selector-class">.6</span><span class="selector-class">.8-py2</span><span class="selector-class">.py3-none-any</span><span class="selector-class">.whl</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="测试安装"><a href="#测试安装" class="headerlink" title="测试安装"></a>测试安装</h2>
                  <p>安装完成之后，可以在Python命令行下测试：</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">$ python3</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; import scrapy_redis</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果没有错误报出，则证明库已经安装好了。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-01-25 23:33:42" itemprop="dateCreated datePublished" datetime="2018-01-25T23:33:42+08:00">2018-01-25</time>
                </span>
                <span id="/5432.html" class="post-meta-item leancloud_visitors" data-flag-title="[Python3网络爬虫开发实战] 1.8.4-Scrapy-Redis的安装" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>557</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>1 分钟</span>
                </span>
              </div>
            </article>
            <script>
              document.querySelectorAll('.random').forEach(item => item.src = "https://picsum.photos/id/" + Math.floor(Math.random() * Math.floor(300)) + "/200/133")

            </script>
            <nav class="pagination">
              <a class="extend prev" rel="prev" href="/page/17/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/17/">17</a><span class="page-number current">18</span><a class="page-number" href="/page/19/">19</a><span class="space">&hellip;</span><a class="page-number" href="/page/31/">31</a><a class="extend next" rel="next" href="/page/19/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
            </nav>
          </div>
          <script>
            window.addEventListener('tabs:register', () =>
            {
              let
              {
                activeClass
              } = CONFIG.comments;
              if (CONFIG.comments.storage)
              {
                activeClass = localStorage.getItem('comments_active') || activeClass;
              }
              if (activeClass)
              {
                let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
                if (activeTab)
                {
                  activeTab.click();
                }
              }
            });
            if (CONFIG.comments.storage)
            {
              window.addEventListener('tabs:click', event =>
              {
                if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
                let commentClass = event.target.classList[1];
                localStorage.setItem('comments_active', commentClass);
              });
            }

          </script>
        </div>
        <div class="toggle sidebar-toggle">
          <span class="toggle-line toggle-line-first"></span>
          <span class="toggle-line toggle-line-middle"></span>
          <span class="toggle-line toggle-line-last"></span>
        </div>
        <aside class="sidebar">
          <div class="sidebar-inner">
            <ul class="sidebar-nav motion-element">
              <li class="sidebar-nav-toc"> 文章目录 </li>
              <li class="sidebar-nav-overview"> 站点概览 </li>
            </ul>
            <!--noindex-->
            <div class="post-toc-wrap sidebar-panel">
            </div>
            <!--/noindex-->
            <div class="site-overview-wrap sidebar-panel">
              <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
                <img class="site-author-image" itemprop="image" alt="崔庆才" src="/images/avatar.png">
                <p class="site-author-name" itemprop="name">崔庆才</p>
                <div class="site-description" itemprop="description">崔庆才的个人站点，记录生活的瞬间，分享学习的心得。</div>
              </div>
              <div class="site-state-wrap motion-element">
                <nav class="site-state">
                  <div class="site-state-item site-state-posts">
                    <a href="/archives/">
                      <span class="site-state-item-count">608</span>
                      <span class="site-state-item-name">日志</span>
                    </a>
                  </div>
                  <div class="site-state-item site-state-categories">
                    <a href="/categories/">
                      <span class="site-state-item-count">24</span>
                      <span class="site-state-item-name">分类</span></a>
                  </div>
                  <div class="site-state-item site-state-tags">
                    <a href="/tags/">
                      <span class="site-state-item-count">156</span>
                      <span class="site-state-item-name">标签</span></a>
                  </div>
                </nav>
              </div>
              <div class="links-of-author motion-element">
                <span class="links-of-author-item">
                  <a href="https://github.com/Germey" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;Germey" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
                </span>
                <span class="links-of-author-item">
                  <a href="mailto:cqc@cuiqingcai.com.com" title="邮件 → mailto:cqc@cuiqingcai.com.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>邮件</a>
                </span>
                <span class="links-of-author-item">
                  <a href="https://weibo.com/cuiqingcai" title="微博 → https:&#x2F;&#x2F;weibo.com&#x2F;cuiqingcai" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>微博</a>
                </span>
                <span class="links-of-author-item">
                  <a href="https://www.zhihu.com/people/Germey" title="知乎 → https:&#x2F;&#x2F;www.zhihu.com&#x2F;people&#x2F;Germey" rel="noopener" target="_blank"><i class="fa fa-magic fa-fw"></i>知乎</a>
                </span>
              </div>
            </div>
            <div style=" width: 100%;" class="sidebar-panel sidebar-panel-image sidebar-panel-active">
              <a href="https://tutorial.lengyue.video/?coupon=12ef4b1a-a3db-11ea-bb37-0242ac130002_cqx_850" target="_blank" rel="noopener">
                <img src="https://qiniu.cuiqingcai.com/bco2a.png" style=" width: 100%;">
              </a>
            </div>
            <div style=" width: 100%;" class="sidebar-panel sidebar-panel-image sidebar-panel-active">
              <a href="http://www.ipidea.net/?utm-source=cqc&utm-keyword=?cqc" target="_blank" rel="noopener">
                <img src="https://qiniu.cuiqingcai.com/0ywun.png" style=" width: 100%;">
              </a>
            </div>
            <div class="sidebar-panel sidebar-panel-tags sidebar-panel-active">
              <h4 class="name"> 标签云 </h4>
              <div class="content">
                <a href="/tags/2048/" style="font-size: 10px;">2048</a> <a href="/tags/API/" style="font-size: 10px;">API</a> <a href="/tags/Bootstrap/" style="font-size: 11.25px;">Bootstrap</a> <a href="/tags/CDN/" style="font-size: 10px;">CDN</a> <a href="/tags/CQC/" style="font-size: 10px;">CQC</a> <a href="/tags/CSS/" style="font-size: 10px;">CSS</a> <a href="/tags/CSS-%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 10px;">CSS 反爬虫</a> <a href="/tags/CV/" style="font-size: 10px;">CV</a> <a href="/tags/Django/" style="font-size: 10px;">Django</a> <a href="/tags/Eclipse/" style="font-size: 11.25px;">Eclipse</a> <a href="/tags/FTP/" style="font-size: 10px;">FTP</a> <a href="/tags/Git/" style="font-size: 10px;">Git</a> <a href="/tags/GitHub/" style="font-size: 13.75px;">GitHub</a> <a href="/tags/HTML5/" style="font-size: 10px;">HTML5</a> <a href="/tags/Hexo/" style="font-size: 10px;">Hexo</a> <a href="/tags/IT/" style="font-size: 10px;">IT</a> <a href="/tags/JSP/" style="font-size: 10px;">JSP</a> <a href="/tags/JavaScript/" style="font-size: 10px;">JavaScript</a> <a href="/tags/K8s/" style="font-size: 10px;">K8s</a> <a href="/tags/LOGO/" style="font-size: 10px;">LOGO</a> <a href="/tags/Linux/" style="font-size: 10px;">Linux</a> <a href="/tags/MIUI/" style="font-size: 10px;">MIUI</a> <a href="/tags/MongoDB/" style="font-size: 10px;">MongoDB</a> <a href="/tags/Mysql/" style="font-size: 10px;">Mysql</a> <a href="/tags/NBA/" style="font-size: 10px;">NBA</a> <a href="/tags/PHP/" style="font-size: 11.25px;">PHP</a> <a href="/tags/PS/" style="font-size: 10px;">PS</a> <a href="/tags/Pathlib/" style="font-size: 10px;">Pathlib</a> <a href="/tags/PhantomJS/" style="font-size: 10px;">PhantomJS</a> <a href="/tags/Python/" style="font-size: 15px;">Python</a> <a href="/tags/Python3/" style="font-size: 12.5px;">Python3</a> <a href="/tags/Pythonic/" style="font-size: 10px;">Pythonic</a> <a href="/tags/QQ/" style="font-size: 10px;">QQ</a> <a href="/tags/Redis/" style="font-size: 10px;">Redis</a> <a href="/tags/SAE/" style="font-size: 10px;">SAE</a> <a href="/tags/SSH/" style="font-size: 10px;">SSH</a> <a href="/tags/SVG/" style="font-size: 10px;">SVG</a> <a href="/tags/Scrapy/" style="font-size: 10px;">Scrapy</a> <a href="/tags/Scrapy-redis/" style="font-size: 10px;">Scrapy-redis</a> <a href="/tags/Scrapy%E5%88%86%E5%B8%83%E5%BC%8F/" style="font-size: 10px;">Scrapy分布式</a> <a href="/tags/Selenium/" style="font-size: 10px;">Selenium</a> <a href="/tags/TKE/" style="font-size: 10px;">TKE</a> <a href="/tags/Ubuntu/" style="font-size: 11.25px;">Ubuntu</a> <a href="/tags/VS-Code/" style="font-size: 10px;">VS Code</a> <a href="/tags/Vs-Code/" style="font-size: 10px;">Vs Code</a> <a href="/tags/Vue/" style="font-size: 11.25px;">Vue</a> <a href="/tags/Webpack/" style="font-size: 10px;">Webpack</a> <a href="/tags/Windows/" style="font-size: 10px;">Windows</a> <a href="/tags/Winpcap/" style="font-size: 10px;">Winpcap</a> <a href="/tags/WordPress/" style="font-size: 13.75px;">WordPress</a> <a href="/tags/Youtube/" style="font-size: 11.25px;">Youtube</a> <a href="/tags/android/" style="font-size: 10px;">android</a> <a href="/tags/ansible/" style="font-size: 10px;">ansible</a> <a href="/tags/cocos2d-x/" style="font-size: 10px;">cocos2d-x</a> <a href="/tags/e6/" style="font-size: 10px;">e6</a> <a href="/tags/fitvids/" style="font-size: 10px;">fitvids</a> <a href="/tags/git/" style="font-size: 11.25px;">git</a> <a href="/tags/json/" style="font-size: 10px;">json</a> <a href="/tags/js%E9%80%86%E5%90%91/" style="font-size: 10px;">js逆向</a> <a href="/tags/kubernetes/" style="font-size: 10px;">kubernetes</a> <a href="/tags/log/" style="font-size: 10px;">log</a> <a href="/tags/logging/" style="font-size: 10px;">logging</a> <a href="/tags/matlab/" style="font-size: 11.25px;">matlab</a> <a href="/tags/python/" style="font-size: 20px;">python</a> <a href="/tags/pytube/" style="font-size: 11.25px;">pytube</a> <a href="/tags/pywin32/" style="font-size: 10px;">pywin32</a> <a href="/tags/style/" style="font-size: 10px;">style</a> <a href="/tags/tomcat/" style="font-size: 10px;">tomcat</a> <a href="/tags/ubuntu/" style="font-size: 10px;">ubuntu</a> <a href="/tags/uwsgi/" style="font-size: 10px;">uwsgi</a> <a href="/tags/vsftpd/" style="font-size: 10px;">vsftpd</a> <a href="/tags/wamp/" style="font-size: 10px;">wamp</a> <a href="/tags/wineQQ/" style="font-size: 10px;">wineQQ</a> <a href="/tags/%E4%B8%83%E7%89%9B/" style="font-size: 11.25px;">七牛</a> <a href="/tags/%E4%B8%8A%E6%B5%B7/" style="font-size: 10px;">上海</a> <a href="/tags/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/" style="font-size: 10px;">个人网站</a> <a href="/tags/%E4%B8%BB%E9%A2%98/" style="font-size: 10px;">主题</a> <a href="/tags/%E4%BA%91%E4%BA%A7%E5%93%81/" style="font-size: 10px;">云产品</a> <a href="/tags/%E4%BA%91%E5%AD%98%E5%82%A8/" style="font-size: 10px;">云存储</a> <a href="/tags/%E4%BA%AC%E4%B8%9C%E4%BA%91/" style="font-size: 10px;">京东云</a> <a href="/tags/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/" style="font-size: 12.5px;">人工智能</a> <a href="/tags/%E4%BB%A3%E7%90%86/" style="font-size: 10px;">代理</a> <a href="/tags/%E4%BB%A3%E7%A0%81/" style="font-size: 10px;">代码</a> <a href="/tags/%E4%BB%A3%E7%A0%81%E5%88%86%E4%BA%AB%E5%9B%BE/" style="font-size: 10px;">代码分享图</a> <a href="/tags/%E4%BC%98%E5%8C%96/" style="font-size: 10px;">优化</a> <a href="/tags/%E4%BD%8D%E8%BF%90%E7%AE%97/" style="font-size: 10px;">位运算</a> <a href="/tags/%E5%85%AC%E4%BC%97%E5%8F%B7/" style="font-size: 10px;">公众号</a> <a href="/tags/%E5%88%86%E4%BA%AB/" style="font-size: 10px;">分享</a> <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/" style="font-size: 10px;">分布式</a> <a href="/tags/%E5%88%9B%E4%B8%9A/" style="font-size: 10px;">创业</a> <a href="/tags/%E5%89%8D%E7%AB%AF/" style="font-size: 12.5px;">前端</a> <a href="/tags/%E5%8D%9A%E5%AE%A2/" style="font-size: 10px;">博客</a> <a href="/tags/%E5%8E%9F%E7%94%9FAPP/" style="font-size: 10px;">原生APP</a> <a href="/tags/%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 12.5px;">反爬虫</a> <a href="/tags/%E5%91%BD%E4%BB%A4/" style="font-size: 10px;">命令</a> <a href="/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E5%B8%83%E5%B1%80/" style="font-size: 10px;">响应式布局</a> <a href="/tags/%E5%9E%83%E5%9C%BE%E9%82%AE%E4%BB%B6/" style="font-size: 10px;">垃圾邮件</a> <a href="/tags/%E5%9F%9F%E5%90%8D%E7%BB%91%E5%AE%9A/" style="font-size: 10px;">域名绑定</a> <a href="/tags/%E5%A4%8D%E7%9B%98/" style="font-size: 10px;">复盘</a> <a href="/tags/%E5%A4%A7%E4%BC%97%E7%82%B9%E8%AF%84/" style="font-size: 10px;">大众点评</a> <a href="/tags/%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 10px;">字体反爬虫</a> <a href="/tags/%E5%AD%97%E7%AC%A6%E9%97%AE%E9%A2%98/" style="font-size: 10px;">字符问题</a> <a href="/tags/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/" style="font-size: 10px;">学习方法</a> <a href="/tags/%E5%AE%89%E5%8D%93/" style="font-size: 10px;">安卓</a> <a href="/tags/%E5%AE%9E%E7%94%A8/" style="font-size: 10px;">实用</a> <a href="/tags/%E5%B0%81%E9%9D%A2/" style="font-size: 10px;">封面</a> <a href="/tags/%E5%B4%94%E5%BA%86%E6%89%8D/" style="font-size: 18.75px;">崔庆才</a> <a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size: 12.5px;">工具</a> <a href="/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/" style="font-size: 10px;">开发工具</a> <a href="/tags/%E5%BE%AE%E8%BD%AF/" style="font-size: 10px;">微软</a> <a href="/tags/%E6%80%9D%E8%80%83/" style="font-size: 10px;">思考</a> <a href="/tags/%E6%89%8B%E6%9C%BA%E8%AE%BF%E9%97%AE/" style="font-size: 10px;">手机访问</a> <a href="/tags/%E6%95%99%E7%A8%8B/" style="font-size: 10px;">教程</a> <a href="/tags/%E6%95%99%E8%82%B2/" style="font-size: 10px;">教育</a> <a href="/tags/%E6%96%B0%E4%B9%A6/" style="font-size: 12.5px;">新书</a> <a href="/tags/%E6%96%B9%E6%B3%95%E8%AE%BA/" style="font-size: 10px;">方法论</a> <a href="/tags/%E6%97%85%E6%B8%B8/" style="font-size: 10px;">旅游</a> <a href="/tags/%E6%97%A5%E5%BF%97/" style="font-size: 10px;">日志</a> <a href="/tags/%E6%9A%97%E6%97%B6%E9%97%B4/" style="font-size: 10px;">暗时间</a> <a href="/tags/%E6%9D%9C%E5%85%B0%E7%89%B9/" style="font-size: 11.25px;">杜兰特</a> <a href="/tags/%E6%A1%8C%E9%9D%A2/" style="font-size: 10px;">桌面</a> <a href="/tags/%E6%AD%8C%E5%8D%95/" style="font-size: 10px;">歌单</a> <a href="/tags/%E6%B1%9F%E5%8D%97/" style="font-size: 10px;">江南</a> <a href="/tags/%E6%B8%B8%E6%88%8F/" style="font-size: 10px;">游戏</a> <a href="/tags/%E7%84%A6%E8%99%91/" style="font-size: 10px;">焦虑</a> <a href="/tags/%E7%88%AC%E8%99%AB/" style="font-size: 16.25px;">爬虫</a> <a href="/tags/%E7%88%AC%E8%99%AB%E4%B9%A6%E7%B1%8D/" style="font-size: 11.25px;">爬虫书籍</a> <a href="/tags/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F/" style="font-size: 10px;">环境变量</a> <a href="/tags/%E7%94%9F%E6%B4%BB%E7%AC%94%E8%AE%B0/" style="font-size: 10px;">生活笔记</a> <a href="/tags/%E7%99%BB%E5%BD%95/" style="font-size: 10px;">登录</a> <a href="/tags/%E7%9F%A5%E4%B9%8E/" style="font-size: 10px;">知乎</a> <a href="/tags/%E7%9F%AD%E4%BF%A1/" style="font-size: 10px;">短信</a> <a href="/tags/%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81/" style="font-size: 10px;">短信验证码</a> <a href="/tags/%E7%AC%94%E8%AE%B0%E8%BD%AF%E4%BB%B6/" style="font-size: 10px;">笔记软件</a> <a href="/tags/%E7%AF%AE%E7%BD%91/" style="font-size: 10px;">篮网</a> <a href="/tags/%E7%BA%B8%E5%BC%A0/" style="font-size: 10px;">纸张</a> <a href="/tags/%E7%BB%84%E4%BB%B6/" style="font-size: 10px;">组件</a> <a href="/tags/%E7%BD%91%E7%AB%99/" style="font-size: 10px;">网站</a> <a href="/tags/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/" style="font-size: 11.25px;">网络爬虫</a> <a href="/tags/%E7%BE%8E%E5%AD%A6/" style="font-size: 10px;">美学</a> <a href="/tags/%E8%82%89%E5%A4%B9%E9%A6%8D/" style="font-size: 10px;">肉夹馍</a> <a href="/tags/%E8%85%BE%E8%AE%AF%E4%BA%91/" style="font-size: 10px;">腾讯云</a> <a href="/tags/%E8%87%AA%E5%BE%8B/" style="font-size: 10px;">自律</a> <a href="/tags/%E8%A5%BF%E5%B0%91%E7%88%B7/" style="font-size: 10px;">西少爷</a> <a href="/tags/%E8%A7%86%E9%A2%91/" style="font-size: 10px;">视频</a> <a href="/tags/%E8%B0%B7%E6%AD%8C%E9%AA%8C%E8%AF%81%E7%A0%81/" style="font-size: 10px;">谷歌验证码</a> <a href="/tags/%E8%BF%90%E8%90%A5/" style="font-size: 10px;">运营</a> <a href="/tags/%E8%BF%9C%E7%A8%8B/" style="font-size: 10px;">远程</a> <a href="/tags/%E9%80%86%E5%90%91/" style="font-size: 10px;">逆向</a> <a href="/tags/%E9%85%8D%E7%BD%AE/" style="font-size: 10px;">配置</a> <a href="/tags/%E9%87%8D%E8%A3%85/" style="font-size: 10px;">重装</a> <a href="/tags/%E9%98%BF%E6%9D%9C/" style="font-size: 10px;">阿杜</a> <a href="/tags/%E9%9D%99%E8%A7%85/" style="font-size: 17.5px;">静觅</a> <a href="/tags/%E9%A2%A0%E8%A6%86/" style="font-size: 10px;">颠覆</a> <a href="/tags/%E9%A3%9E%E4%BF%A1/" style="font-size: 10px;">飞信</a> <a href="/tags/%E9%B8%BF%E8%92%99/" style="font-size: 10px;">鸿蒙</a>
              </div>
              <script>
                const tagsColors = ['#00a67c', '#5cb85c', '#d9534f', '#567e95', '#b37333', '#f4843d', '#15a287']
                const tagsElements = document.querySelectorAll('.sidebar-panel-tags .content a')
                tagsElements.forEach((item) =>
                {
                  item.style.backgroundColor = tagsColors[Math.floor(Math.random() * tagsColors.length)]
                })

              </script>
            </div>
            <div class="sidebar-panel sidebar-panel-categories sidebar-panel-active">
              <h4 class="name"> 分类 </h4>
              <div class="content">
                <ul class="category-list">
                  <li class="category-list-item"><a class="category-list-link" href="/categories/C-C/">C/C++</a><span class="category-list-count">23</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/HTML/">HTML</a><span class="category-list-count">14</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Java/">Java</a><span class="category-list-count">5</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/JavaScript/">JavaScript</a><span class="category-list-count">26</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Linux/">Linux</a><span class="category-list-count">15</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Markdown/">Markdown</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Net/">Net</a><span class="category-list-count">4</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Other/">Other</a><span class="category-list-count">39</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/PHP/">PHP</a><span class="category-list-count">27</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Paper/">Paper</a><span class="category-list-count">2</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Python/">Python</a><span class="category-list-count">261</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/TypeScript/">TypeScript</a><span class="category-list-count">2</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E5%B1%95%E7%A4%BA/">个人展示</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E6%97%A5%E8%AE%B0/">个人日记</a><span class="category-list-count">9</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E8%AE%B0%E5%BD%95/">个人记录</a><span class="category-list-count">4</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E9%9A%8F%E7%AC%94/">个人随笔</a><span class="category-list-count">15</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/">安装配置</a><span class="category-list-count">59</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E6%8A%80%E6%9C%AF%E6%9D%82%E8%B0%88/">技术杂谈</a><span class="category-list-count">88</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E6%9C%AA%E5%88%86%E7%B1%BB/">未分类</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E7%94%9F%E6%B4%BB%E7%AC%94%E8%AE%B0/">生活笔记</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E7%A6%8F%E5%88%A9%E4%B8%93%E5%8C%BA/">福利专区</a><span class="category-list-count">6</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E8%81%8C%E4%BD%8D%E6%8E%A8%E8%8D%90/">职位推荐</a><span class="category-list-count">2</span></li>
                </ul>
              </div>
            </div>
            <div class="sidebar-panel sidebar-panel-friends sidebar-panel-active">
              <h4 class="name"> 友情链接 </h4>
              <ul class="friends">
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/j2dub.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.findhao.net/" target="_blank" rel="noopener">FindHao</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ou6mm.jpg">
                  </span>
                  <span class="link">
                    <a href="https://diygod.me/" target="_blank" rel="noopener">DIYgod</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/6apxu.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.51dev.com/" target="_blank" rel="noopener">IT技术社区</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.jankl.com/img/titleshu.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.jankl.com/" target="_blank" rel="noopener">liberalist</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/bqlbs.png">
                  </span>
                  <span class="link">
                    <a href="http://www.urselect.com/" target="_blank" rel="noopener">优社电商</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/8s88c.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.yuanrenxue.com/" target="_blank" rel="noopener">猿人学</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/2wgg5.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.yunlifang.cn/" target="_blank" rel="noopener">云立方</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/shwr6.png">
                  </span>
                  <span class="link">
                    <a href="http://lanbing510.info/" target="_blank" rel="noopener">冰蓝</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/blvoh.jpg">
                  </span>
                  <span class="link">
                    <a href="https://lengyue.me/" target="_blank" rel="noopener">冷月</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="http://qianxunclub.com/favicon.png">
                  </span>
                  <span class="link">
                    <a href="http://qianxunclub.com/" target="_blank" rel="noopener">千寻啊千寻</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/0044u.jpg">
                  </span>
                  <span class="link">
                    <a href="http://kodcloud.com/" target="_blank" rel="noopener">可道云</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ygnpn.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.kunkundashen.cn/" target="_blank" rel="noopener">坤坤大神</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/22uv1.png">
                  </span>
                  <span class="link">
                    <a href="http://www.cenchong.com/" target="_blank" rel="noopener">岑冲博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ev9kl.png">
                  </span>
                  <span class="link">
                    <a href="http://www.zxiaoji.com/" target="_blank" rel="noopener">张小鸡</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.503error.com/favicon.ico">
                  </span>
                  <span class="link">
                    <a href="https://www.503error.com/" target="_blank" rel="noopener">张志明个人博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/x714o.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.hubwiz.com/" target="_blank" rel="noopener">汇智网</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/129d8.png">
                  </span>
                  <span class="link">
                    <a href="https://www.bysocket.com/" target="_blank" rel="noopener">泥瓦匠BYSocket</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.xiongge.club/favicon.ico">
                  </span>
                  <span class="link">
                    <a href="https://www.xiongge.club/" target="_blank" rel="noopener">熊哥club</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/3w4fe.png">
                  </span>
                  <span class="link">
                    <a href="https://zerlong.com/" target="_blank" rel="noopener">知语</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/44hxf.png">
                  </span>
                  <span class="link">
                    <a href="http://redstonewill.com/" target="_blank" rel="noopener">红色石头</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/8g1fk.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.laodong.me/" target="_blank" rel="noopener">老董博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/wkaus.jpg">
                  </span>
                  <span class="link">
                    <a href="https://zhaoshuai.me/" target="_blank" rel="noopener">碎念</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/pgo0r.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.chenwenguan.com/" target="_blank" rel="noopener">陈文管的博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/kk82a.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.lxlinux.net/" target="_blank" rel="noopener">良许Linux教程网</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/lj0t2.jpg">
                  </span>
                  <span class="link">
                    <a href="https://tanqingbo.cn/" target="_blank" rel="noopener">IT码农</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/i8cdr.png">
                  </span>
                  <span class="link">
                    <a href="https://junyiseo.com/" target="_blank" rel="noopener">均益个人博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/chwv2.png">
                  </span>
                  <span class="link">
                    <a href="https://brucedone.com/" target="_blank" rel="noopener">大鱼的鱼塘</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/2y43o.png">
                  </span>
                  <span class="link">
                    <a href="http://bbs.nightteam.cn/" target="_blank" rel="noopener">夜幕爬虫安全论坛</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/zvc3w.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.weishidong.com/" target="_blank" rel="noopener">韦世东的技术专栏</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ebudy.jpg">
                  </span>
                  <span class="link">
                    <a href="https://chuanjiabing.com/" target="_blank" rel="noopener">穿甲兵技术社区</a>
                  </span>
                </li>
              </ul>
            </div>
          </div>
        </aside>
        <div id="sidebar-dimmer"></div>
      </div>
    </main>
    <footer class="footer">
      <div class="footer-inner">
        <div class="copyright"> &copy; <span itemprop="copyrightYear">2021</span>
          <span class="with-love">
            <i class="fa fa-heart"></i>
          </span>
          <span class="author" itemprop="copyrightHolder">崔庆才丨静觅</span>
          <span class="post-meta-divider">|</span>
          <span class="post-meta-item-icon">
            <i class="fa fa-chart-area"></i>
          </span>
          <span title="站点总字数">2.6m</span>
          <span class="post-meta-divider">|</span>
          <span class="post-meta-item-icon">
            <i class="fa fa-coffee"></i>
          </span>
          <span title="站点阅读时长">39:54</span>
        </div>
        <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动 </div>
        <div class="beian"><a href="https://beian.miit.gov.cn/" rel="noopener" target="_blank">京ICP备18015597号-1 </a>
        </div>
        <script>
          (function ()
          {
            function leancloudSelector(url)
            {
              url = encodeURI(url);
              return document.getElementById(url).querySelector('.leancloud-visitors-count');
            }

            function addCount(Counter)
            {
              var visitors = document.querySelector('.leancloud_visitors');
              var url = decodeURI(visitors.id);
              var title = visitors.dataset.flagTitle;
              Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify(
              {
                url
              }))).then(response => response.json()).then((
              {
                results
              }) =>
              {
                if (results.length > 0)
                {
                  var counter = results[0];
                  leancloudSelector(url).innerText = counter.time + 1;
                  Counter('put', '/classes/Counter/' + counter.objectId,
                  {
                    time:
                    {
                      '__op': 'Increment',
                      'amount': 1
                    }
                  }).catch(error =>
                  {
                    console.error('Failed to save visitor count', error);
                  });
                }
                else
                {
                  Counter('post', '/classes/Counter',
                  {
                    title,
                    url,
                    time: 1
                  }).then(response => response.json()).then(() =>
                  {
                    leancloudSelector(url).innerText = 1;
                  }).catch(error =>
                  {
                    console.error('Failed to create', error);
                  });
                }
              }).catch(error =>
              {
                console.error('LeanCloud Counter Error', error);
              });
            }

            function showTime(Counter)
            {
              var visitors = document.querySelectorAll('.leancloud_visitors');
              var entries = [...visitors].map(element =>
              {
                return decodeURI(element.id);
              });
              Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify(
              {
                url:
                {
                  '$in': entries
                }
              }))).then(response => response.json()).then((
              {
                results
              }) =>
              {
                for (let url of entries)
                {
                  let target = results.find(item => item.url === url);
                  leancloudSelector(url).innerText = target ? target.time : 0;
                }
              }).catch(error =>
              {
                console.error('LeanCloud Counter Error', error);
              });
            }
            let
            {
              app_id,
              app_key,
              server_url
            } = {
              "enable": true,
              "app_id": "6X5dRQ0pnPWJgYy8SXOg0uID-gzGzoHsz",
              "app_key": "ziLDVEy73ne5HtFTiGstzHMS",
              "server_url": "https://6x5drq0p.lc-cn-n1-shared.com",
              "security": false
            };

            function fetchData(api_server)
            {
              var Counter = (method, url, data) =>
              {
                return fetch(`${api_server}/1.1${url}`,
                {
                  method,
                  headers:
                  {
                    'X-LC-Id': app_id,
                    'X-LC-Key': app_key,
                    'Content-Type': 'application/json',
                  },
                  body: JSON.stringify(data)
                });
              };
              if (CONFIG.page.isPost)
              {
                if (CONFIG.hostname !== location.hostname) return;
                addCount(Counter);
              }
              else if (document.querySelectorAll('.post-title-link').length >= 1)
              {
                showTime(Counter);
              }
            }
            let api_server = app_id.slice(-9) !== '-MdYXbMMI' ? server_url : `https://${app_id.slice(0, 8).toLowerCase()}.api.lncldglobal.com`;
            if (api_server)
            {
              fetchData(api_server);
            }
            else
            {
              fetch('https://app-router.leancloud.cn/2/route?appId=' + app_id).then(response => response.json()).then((
              {
                api_server
              }) =>
              {
                fetchData('https://' + api_server);
              });
            }
          })();

        </script>
      </div>
      <div class="footer-stat">
        <span id="cnzz_stat_icon_1279355174"></span>
        <script type="text/javascript">
          document.write(unescape("%3Cspan id='cnzz_stat_icon_1279355174'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279355174%26online%3D1%26show%3Dline' type='text/javascript'%3E%3C/script%3E"));

        </script>
      </div>
    </footer>
  </div>
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js"></script>
  <script src="/js/utils.js"></script>
  <script src="/.js"></script>
  <script src="/js/schemes/pisces.js"></script>
  <script src="/.js"></script>
  <script src="/js/next-boot.js"></script>
  <script src="/.js"></script>
  <script>
    (function ()
    {
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x = document.getElementsByTagName("link");
      //Find the last canonical URL
      if (x.length > 0)
      {
        for (i = 0; i < x.length; i++)
        {
          if (x[i].rel.toLowerCase() == 'canonical' && x[i].href)
          {
            canonicalURL = x[i].href;
          }
        }
      }
      //Get protocol
      if (!canonicalURL)
      {
        curProtocol = window.location.protocol.split(':')[0];
      }
      else
      {
        curProtocol = canonicalURL.split(':')[0];
      }
      //Get current URL if the canonical URL does not exist
      if (!canonicalURL) canonicalURL = window.location.href;
      //Assign script content. Replace current URL with the canonical URL
      ! function ()
      {
        var e = /([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,
          r = canonicalURL,
          t = document.referrer;
        if (!e.test(r))
        {
          var n = (String(curProtocol).toLowerCase() === 'https') ? "https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif" : "//api.share.baidu.com/s.gif";
          t ? (n += "?r=" + encodeURIComponent(document.referrer), r && (n += "&l=" + r)) : r && (n += "?l=" + r);
          var i = new Image;
          i.src = n
        }
      }(window);
    })();

  </script>
  <script src="/js/local-search.js"></script>
  <script src="/.js"></script>
</body>

</html>
